(77d1794a) Tester's build January 10th, 2020

This commit is contained in:
juanjp600
2020-01-10 14:42:38 -03:00
parent c02da46ef5
commit e6a08d715b
4529 changed files with 1145046 additions and 218950 deletions
@@ -0,0 +1,93 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
namespace Barotrauma
{
class BlurEffect
{
public readonly Effect Effect;
public BlurEffect(Effect effect, float dx, float dy)
{
Effect = effect;
SetParameters(dx, dy);
}
/// <summary>
/// Computes sample weightings and texture coordinate offsets
/// for one pass of a separable gaussian blur filter.
/// </summary>
public void SetParameters(float dx, float dy)
{
EffectParameter weightsParameter = Effect.Parameters["SampleWeights"];
EffectParameter offsetsParameter = Effect.Parameters["SampleOffsets"];
// Look up how many samples our gaussian blur effect supports.
int sampleCount = weightsParameter.Elements.Count;
// Create temporary arrays for computing our filter settings.
float[] sampleWeights = new float[sampleCount];
Vector2[] sampleOffsets = new Vector2[sampleCount];
sampleWeights[0] = ComputeGaussian(0);
sampleOffsets[0] = new Vector2(0);
float totalWeights = sampleWeights[0];
// Add pairs of additional sample taps, positioned
// along a line in both directions from the center.
for (int i = 0; i < sampleCount / 2; i++)
{
// Store weights for the positive and negative taps.
float weight = ComputeGaussian(i + 1);
sampleWeights[i * 2 + 1] = weight;
sampleWeights[i * 2 + 2] = weight;
totalWeights += weight * 2;
// To get the maximum amount of blurring from a limited number of
// pixel shader samples, we take advantage of the bilinear filtering
// hardware inside the texture fetch unit. If we position our texture
// coordinates exactly halfway between two texels, the filtering unit
// will average them for us, giving two samples for the price of one.
// This allows us to step in units of two texels per sample, rather
// than just one at a time. The 1.5 offset kicks things off by
// positioning us nicely in between two texels.
float sampleOffset = i * 2 + 1.5f;
Vector2 delta = new Vector2(dx, dy) * sampleOffset;
// Store texture coordinate offsets for the positive and negative taps.
sampleOffsets[i * 2 + 1] = delta;
sampleOffsets[i * 2 + 2] = -delta;
}
// Normalize the list of sample weightings, so they will always sum to one.
for (int i = 0; i < sampleWeights.Length; i++)
{
sampleWeights[i] /= totalWeights;
}
weightsParameter.SetValue(sampleWeights);
offsetsParameter.SetValue(sampleOffsets);
}
/// <summary>
/// Evaluates a single point on the gaussian falloff curve.
/// Used for setting up the blur filter weightings.
/// </summary>
float ComputeGaussian(float n)
{
float theta = 2.0f;
return (float)((1.0 / Math.Sqrt(2 * Math.PI * theta)) *
Math.Exp(-(n * n) / (2 * theta * theta)));
}
}
}
@@ -0,0 +1,576 @@
using Barotrauma.Tutorials;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class CampaignSetupUI
{
private GUIComponent newGameContainer, loadGameContainer;
private GUIListBox subList;
private GUIListBox saveList;
private GUITextBox saveNameBox, seedBox;
private GUILayoutGroup subPreviewContainer;
private GUIButton loadGameButton, deleteMpSaveButton;
public Action<Submarine, string, string> StartNewGame;
public Action<string> LoadGame;
private readonly bool isMultiplayer;
public CampaignSetupUI(bool isMultiplayer, GUIComponent newGameContainer, GUIComponent loadGameContainer, IEnumerable<Submarine> submarines, IEnumerable<string> saveFiles = null)
{
this.isMultiplayer = isMultiplayer;
this.newGameContainer = newGameContainer;
this.loadGameContainer = loadGameContainer;
var columnContainer = new GUILayoutGroup(new RectTransform(Vector2.One, newGameContainer.RectTransform), isHorizontal: true)
{
Stretch = true,
RelativeSpacing = isMultiplayer ? 0.0f : 0.05f
};
var leftColumn = new GUILayoutGroup(new RectTransform(Vector2.One, columnContainer.RectTransform))
{
Stretch = true,
RelativeSpacing = 0.015f
};
var rightColumn = new GUILayoutGroup(new RectTransform(isMultiplayer ? Vector2.Zero : new Vector2(1.5f, 1.0f), columnContainer.RectTransform))
{
Stretch = true,
RelativeSpacing = 0.015f
};
columnContainer.Recalculate();
// New game left side
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.02f), leftColumn.RectTransform) { MinSize = new Point(0, 20) }, TextManager.Get("SaveName"));
saveNameBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform) { MinSize = new Point(0, 20) }, string.Empty)
{
textFilterFunction = (string str) => { return ToolBox.RemoveInvalidFileNameChars(str); }
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.02f), leftColumn.RectTransform) { MinSize = new Point(0, 20) }, TextManager.Get("MapSeed"));
seedBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform) { MinSize = new Point(0, 20) }, ToolBox.RandomSeed(8));
if (!isMultiplayer)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.02f), leftColumn.RectTransform) { MinSize = new Point(0, 20) }, TextManager.Get("SelectedSub"));
var filterContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), isHorizontal: true)
{
Stretch = true
};
subList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.65f), leftColumn.RectTransform)) { ScrollBarVisible = true };
var searchTitle = new GUITextBlock(new RectTransform(new Vector2(0.001f, 1.0f), filterContainer.RectTransform), TextManager.Get("serverlog.filter"), textAlignment: Alignment.CenterLeft, font: GUI.Font);
var searchBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 1.0f), filterContainer.RectTransform, Anchor.CenterRight), font: GUI.Font);
searchBox.OnSelected += (sender, userdata) => { searchTitle.Visible = false; };
searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; };
searchBox.OnTextChanged += (textBox, text) => { FilterSubs(subList, text); return true; };
var clearButton = new GUIButton(new RectTransform(new Vector2(0.075f, 1.0f), filterContainer.RectTransform), "x")
{
OnClicked = (btn, userdata) => { searchBox.Text = ""; FilterSubs(subList, ""); searchBox.Flash(Color.White); return true; }
};
subList.OnSelected = OnSubSelected;
}
else // Spacing to fix the multiplayer campaign setup layout
{
//spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.25f), leftColumn.RectTransform), style: null);
}
// New game right side
subPreviewContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.8f), rightColumn.RectTransform))
{
Stretch = true
};
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.13f),
(isMultiplayer ? leftColumn : rightColumn).RectTransform) { MaxSize = new Point(int.MaxValue, 60) }, childAnchor: Anchor.TopRight);
var startButton = new GUIButton(new RectTransform(isMultiplayer ? new Vector2(0.5f, 1.0f) : Vector2.One,
buttonContainer.RectTransform, Anchor.BottomRight) { MaxSize = new Point(350, 60) },
TextManager.Get("StartCampaignButton"), style: "GUIButtonLarge")
{
OnClicked = (GUIButton btn, object userData) =>
{
if (string.IsNullOrWhiteSpace(saveNameBox.Text))
{
saveNameBox.Flash(Color.Red);
return false;
}
Submarine selectedSub = null;
if (!isMultiplayer)
{
if (!(subList.SelectedData is Submarine)) { return false; }
selectedSub = subList.SelectedData as Submarine;
}
else
{
if (GameMain.NetLobbyScreen.SelectedSub == null) { return false; }
selectedSub = GameMain.NetLobbyScreen.SelectedSub;
}
if (string.IsNullOrEmpty(selectedSub.MD5Hash.Hash))
{
((GUITextBlock)subList.SelectedComponent).TextColor = Color.DarkRed * 0.8f;
subList.SelectedComponent.CanBeFocused = false;
subList.Deselect();
return false;
}
string savePath = SaveUtil.CreateSavePath(isMultiplayer ? SaveUtil.SaveType.Multiplayer : SaveUtil.SaveType.Singleplayer, saveNameBox.Text);
bool hasRequiredContentPackages = selectedSub.RequiredContentPackagesInstalled;
if (selectedSub.HasTag(SubmarineTag.Shuttle) || !hasRequiredContentPackages)
{
if (!hasRequiredContentPackages)
{
var msgBox = new GUIMessageBox(TextManager.Get("ContentPackageMismatch"),
TextManager.GetWithVariable("ContentPackageMismatchWarning", "[requiredcontentpackages]", string.Join(", ", selectedSub.RequiredContentPackages)),
new string[] { TextManager.Get("Yes"), TextManager.Get("No") });
msgBox.Buttons[0].OnClicked = msgBox.Close;
msgBox.Buttons[0].OnClicked += (button, obj) =>
{
if (GUIMessageBox.MessageBoxes.Count == 0)
{
StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text);
if (isMultiplayer)
{
CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup");
}
}
return true;
};
msgBox.Buttons[1].OnClicked = msgBox.Close;
}
if (selectedSub.HasTag(SubmarineTag.Shuttle))
{
var msgBox = new GUIMessageBox(TextManager.Get("ShuttleSelected"),
TextManager.Get("ShuttleWarning"),
new string[] { TextManager.Get("Yes"), TextManager.Get("No") });
msgBox.Buttons[0].OnClicked = (button, obj) =>
{
StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text);
if (isMultiplayer)
{
CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup");
}
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked = msgBox.Close;
return false;
}
}
else
{
StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text);
if (isMultiplayer)
{
CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup");
}
}
return true;
}
};
if (!isMultiplayer)
{
var disclaimerBtn = new GUIButton(new RectTransform(new Vector2(1.0f, 0.8f), rightColumn.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point(5) }, style: "GUINotificationButton")
{
IgnoreLayoutGroups = true,
OnClicked = (btn, userdata) => { GameMain.Instance.ShowCampaignDisclaimer(); return true; }
};
disclaimerBtn.RectTransform.MaxSize = new Point((int)(30 * GUI.Scale));
}
leftColumn.Recalculate();
rightColumn.Recalculate();
if (submarines != null) { UpdateSubList(submarines); }
UpdateLoadMenu(saveFiles);
}
public void RandomizeSeed()
{
seedBox.Text = ToolBox.RandomSeed(8);
}
private void FilterSubs(GUIListBox subList, string filter)
{
foreach (GUIComponent child in subList.Content.Children)
{
var sub = child.UserData as Submarine;
if (sub == null) { return; }
child.Visible = string.IsNullOrEmpty(filter) ? true : sub.Name.ToLower().Contains(filter.ToLower());
}
}
private bool OnSubSelected(GUIComponent component, object obj)
{
if (subPreviewContainer == null) { return false; }
subPreviewContainer.ClearChildren();
Submarine sub = obj as Submarine;
if (sub == null) { return true; }
sub.CreatePreviewWindow(subPreviewContainer);
return true;
}
private IEnumerable<object> WaitForCampaignSetup()
{
string headerText = TextManager.Get("CampaignStartingPleaseWait");
var msgBox = new GUIMessageBox(headerText, TextManager.Get("CampaignStarting"), new string[] { TextManager.Get("Cancel") });
msgBox.Buttons[0].OnClicked = (btn, userdata) =>
{
GameMain.NetLobbyScreen.HighlightMode(GameMain.NetLobbyScreen.SelectedModeIndex);
GameMain.NetLobbyScreen.SelectMode(GameMain.NetLobbyScreen.SelectedModeIndex);
CoroutineManager.StopCoroutines("WaitForCampaignSetup");
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 10);
while (GameMain.NetLobbyScreen.CampaignUI == null && DateTime.Now < timeOut)
{
msgBox.Header.Text = headerText + new string('.', ((int)Timing.TotalTime % 3 + 1));
yield return CoroutineStatus.Running;
}
msgBox.Close();
yield return CoroutineStatus.Success;
}
public void CreateDefaultSaveName()
{
string savePath = SaveUtil.CreateSavePath(isMultiplayer ? SaveUtil.SaveType.Multiplayer : SaveUtil.SaveType.Singleplayer);
saveNameBox.Text = Path.GetFileNameWithoutExtension(savePath);
}
public void UpdateSubList(IEnumerable<Submarine> submarines)
{
#if !DEBUG
var subsToShow = submarines.Where(s => !s.HasTag(SubmarineTag.HideInMenus));
#else
var subsToShow = submarines;
#endif
subList.ClearChildren();
foreach (Submarine sub in subsToShow)
{
var textBlock = new GUITextBlock(
new RectTransform(new Vector2(1, 0.1f), subList.Content.RectTransform) { MinSize = new Point(0, 30) },
ToolBox.LimitString(sub.Name, GUI.Font, subList.Rect.Width - 65), style: "ListBoxElement")
{
ToolTip = sub.Description,
UserData = sub
};
if(!sub.RequiredContentPackagesInstalled)
{
textBlock.TextColor = Color.Lerp(textBlock.TextColor, Color.DarkRed, .5f);
textBlock.ToolTip = TextManager.Get("ContentPackageMismatch") + "\n\n" + textBlock.RawToolTip;
}
if (sub.HasTag(SubmarineTag.Shuttle))
{
textBlock.TextColor = textBlock.TextColor * 0.85f;
var shuttleText = new GUITextBlock(new RectTransform(new Point(100, textBlock.Rect.Height), textBlock.RectTransform, Anchor.CenterRight)
{
IsFixedSize = false
},
TextManager.Get("Shuttle", fallBackTag: "RespawnShuttle"), textAlignment: Alignment.Right, font: GUI.SmallFont)
{
TextColor = textBlock.TextColor * 0.8f,
ToolTip = textBlock.RawToolTip
};
}
}
if (Submarine.SavedSubmarines.Any())
{
var nonShuttles = subsToShow.Where(s => !s.HasTag(SubmarineTag.Shuttle)).ToList();
if (nonShuttles.Count > 0)
{
subList.Select(nonShuttles[Rand.Int(nonShuttles.Count)]);
}
}
}
private List<string> prevSaveFiles;
public void UpdateLoadMenu(IEnumerable<string> saveFiles = null)
{
prevSaveFiles?.Clear();
loadGameContainer.ClearChildren();
if (saveFiles == null)
{
saveFiles = SaveUtil.GetSaveFiles(isMultiplayer ? SaveUtil.SaveType.Multiplayer : SaveUtil.SaveType.Singleplayer);
}
var leftColumn = new GUILayoutGroup(new RectTransform(isMultiplayer ? new Vector2(1.0f, 0.85f) : new Vector2(0.5f, 1.0f), loadGameContainer.RectTransform), childAnchor: Anchor.TopCenter)
{
Stretch = true,
RelativeSpacing = 0.03f
};
saveList = new GUIListBox(new RectTransform(Vector2.One, leftColumn.RectTransform))
{
OnSelected = SelectSaveFile
};
if (!isMultiplayer)
{
new GUIButton(new RectTransform(new Vector2(0.6f, 0.08f), leftColumn.RectTransform), TextManager.Get("showinfolder"))
{
OnClicked = (btn, userdata) =>
{
System.Diagnostics.Process.Start(SaveUtil.SaveFolder);
return true;
}
};
}
foreach (string saveFile in saveFiles)
{
string fileName = saveFile;
string subName = "";
string saveTime = "";
string contentPackageStr = "";
var saveFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), saveList.Content.RectTransform) { MinSize = new Point(0, 45) }, style: "ListBoxElement")
{
UserData = saveFile
};
var nameText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform), "")
{
CanBeFocused = false
};
if (!isMultiplayer)
{
nameText.Text = Path.GetFileNameWithoutExtension(saveFile);
XDocument doc = SaveUtil.LoadGameSessionDoc(saveFile);
if (doc?.Root == null)
{
DebugConsole.ThrowError("Error loading save file \"" + saveFile + "\". The file may be corrupted.");
nameText.TextColor = Color.Red;
continue;
}
subName = doc.Root.GetAttributeString("submarine", "");
saveTime = doc.Root.GetAttributeString("savetime", "");
contentPackageStr = doc.Root.GetAttributeString("selectedcontentpackages", "");
prevSaveFiles?.Add(saveFile);
}
else
{
string[] splitSaveFile = saveFile.Split(';');
saveFrame.UserData = splitSaveFile[0];
fileName = nameText.Text = Path.GetFileNameWithoutExtension(splitSaveFile[0]);
prevSaveFiles?.Add(fileName);
if (splitSaveFile.Length > 1) { subName = splitSaveFile[1]; }
if (splitSaveFile.Length > 2) { saveTime = splitSaveFile[2]; }
if (splitSaveFile.Length > 3) { contentPackageStr = splitSaveFile[3]; }
}
if (!string.IsNullOrEmpty(saveTime) && long.TryParse(saveTime, out long unixTime))
{
DateTime time = ToolBox.Epoch.ToDateTime(unixTime);
saveTime = time.ToString();
}
if (!string.IsNullOrEmpty(contentPackageStr))
{
List<string> contentPackagePaths = contentPackageStr.Split('|').ToList();
if (!GameSession.IsCompatibleWithSelectedContentPackages(contentPackagePaths, out string errorMsg))
{
nameText.TextColor = Color.Red;
saveFrame.ToolTip = string.Join("\n", errorMsg, TextManager.Get("campaignmode.contentpackagemismatchwarning"));
}
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform, Anchor.BottomLeft),
text: subName, font: GUI.SmallFont)
{
CanBeFocused = false,
UserData = fileName
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), saveFrame.RectTransform),
text: saveTime, textAlignment: Alignment.Right, font: GUI.SmallFont)
{
CanBeFocused = false,
UserData = fileName
};
}
saveList.Content.RectTransform.SortChildren((c1, c2) =>
{
string file1 = c1.GUIComponent.UserData as string;
string file2 = c2.GUIComponent.UserData as string;
DateTime file1WriteTime = DateTime.MinValue;
DateTime file2WriteTime = DateTime.MinValue;
try
{
file1WriteTime = File.GetLastWriteTime(file1);
}
catch
{
//do nothing - DateTime.MinValue will be used and the element will get sorted at the bottom of the list
};
try
{
file2WriteTime = File.GetLastWriteTime(file2);
}
catch
{
//do nothing - DateTime.MinValue will be used and the element will get sorted at the bottom of the list
};
return file2WriteTime.CompareTo(file1WriteTime);
});
loadGameButton = new GUIButton(new RectTransform(new Vector2(0.45f, 0.12f), loadGameContainer.RectTransform, Anchor.BottomRight), TextManager.Get("LoadButton"), style: "GUIButtonLarge")
{
OnClicked = (btn, obj) =>
{
if (string.IsNullOrWhiteSpace(saveList.SelectedData as string)) { return false; }
LoadGame?.Invoke(saveList.SelectedData as string);
if (isMultiplayer)
{
CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup");
}
return true;
},
Enabled = false
};
deleteMpSaveButton = new GUIButton(new RectTransform(new Vector2(0.45f, 0.12f), loadGameContainer.RectTransform, Anchor.BottomLeft), TextManager.Get("Delete"), style: "GUIButtonLarge")
{
OnClicked = DeleteSave,
Visible = false
};
}
private bool SelectSaveFile(GUIComponent component, object obj)
{
string fileName = (string)obj;
if (isMultiplayer)
{
loadGameButton.Enabled = true;
deleteMpSaveButton.Visible = deleteMpSaveButton.Enabled = GameMain.Client.IsServerOwner;
deleteMpSaveButton.Enabled = GameMain.GameSession?.SavePath != fileName;
if (deleteMpSaveButton.Visible)
{
deleteMpSaveButton.UserData = obj as string;
}
return true;
}
XDocument doc = SaveUtil.LoadGameSessionDoc(fileName);
if (doc == null)
{
DebugConsole.ThrowError("Error loading save file \"" + fileName + "\". The file may be corrupted.");
return false;
}
loadGameButton.Enabled = true;
RemoveSaveFrame();
string subName = doc.Root.GetAttributeString("submarine", "");
string saveTime = doc.Root.GetAttributeString("savetime", "unknown");
if (long.TryParse(saveTime, out long unixTime))
{
DateTime time = ToolBox.Epoch.ToDateTime(unixTime);
saveTime = time.ToString();
}
string mapseed = doc.Root.GetAttributeString("mapseed", "unknown");
var saveFileFrame = new GUIFrame(new RectTransform(new Vector2(0.45f, 0.6f), loadGameContainer.RectTransform, Anchor.TopRight)
{
RelativeOffset = new Vector2(0.0f, 0.1f)
}, style: "InnerFrame")
{
UserData = "savefileframe"
};
new GUITextBlock(new RectTransform(new Vector2(1, 0.2f), saveFileFrame.RectTransform, Anchor.TopCenter)
{
RelativeOffset = new Vector2(0, 0.05f)
},
Path.GetFileNameWithoutExtension(fileName), font: GUI.LargeFont, textAlignment: Alignment.Center);
var layoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.5f), saveFileFrame.RectTransform, Anchor.Center)
{
RelativeOffset = new Vector2(0, 0.1f)
});
new GUITextBlock(new RectTransform(new Vector2(1, 0), layoutGroup.RectTransform), $"{TextManager.Get("Submarine")} : {subName}", font: GUI.SmallFont);
new GUITextBlock(new RectTransform(new Vector2(1, 0), layoutGroup.RectTransform), $"{TextManager.Get("LastSaved")} : {saveTime}", font: GUI.SmallFont);
new GUITextBlock(new RectTransform(new Vector2(1, 0), layoutGroup.RectTransform), $"{TextManager.Get("MapSeed")} : {mapseed}", font: GUI.SmallFont);
new GUIButton(new RectTransform(new Vector2(0.4f, 0.15f), saveFileFrame.RectTransform, Anchor.BottomCenter)
{
RelativeOffset = new Vector2(0, 0.1f)
}, TextManager.Get("Delete"))
{
UserData = fileName,
OnClicked = DeleteSave
};
return true;
}
private bool DeleteSave(GUIButton button, object obj)
{
string saveFile = obj as string;
if (obj == null) { return false; }
SaveUtil.DeleteSave(saveFile);
prevSaveFiles?.Remove(saveFile);
UpdateLoadMenu(prevSaveFiles);
return true;
}
private void RemoveSaveFrame()
{
GUIComponent prevFrame = null;
foreach (GUIComponent child in loadGameContainer.Children)
{
if (child.UserData as string != "savefileframe") continue;
prevFrame = child;
break;
}
loadGameContainer.RemoveChild(prevFrame);
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,64 @@
using Microsoft.Xna.Framework;
using System;
using System.Xml.Linq;
namespace Barotrauma
{
class CreditsPlayer : GUIComponent
{
private GUIListBox listBox;
private XElement configElement;
private float scrollSpeed;
public CreditsPlayer(RectTransform rectT, string configFile) : base(null, rectT)
{
GameMain.Instance.OnResolutionChanged += () => { ClearChildren(); Load(); };
var doc = XMLExtensions.TryLoadXml(configFile);
if (doc == null) { return; }
configElement = doc.Root;
Load();
}
private void Load()
{
scrollSpeed = configElement.GetAttributeFloat("scrollspeed", 100.0f);
int spacing = configElement.GetAttributeInt("spacing", 0);
listBox = new GUIListBox(new RectTransform(Vector2.One, RectTransform), style: null)
{
Spacing = spacing
};
foreach (XElement subElement in configElement.Elements())
{
GUIComponent.FromXML(subElement, listBox.Content.RectTransform);
}
foreach (GUIComponent child in listBox.Content.Children)
{
child.CanBeFocused = false;
}
listBox.RecalculateChildren();
listBox.UpdateScrollBarSize();
}
public void Restart()
{
listBox.BarScroll = 0.0f;
}
protected override void Update(float deltaTime)
{
listBox.BarScroll += scrollSpeed / listBox.TotalSize * deltaTime;
if (listBox.BarScroll >= 1.0f)
{
listBox.BarScroll = 0.0f;
}
}
}
}
@@ -0,0 +1,386 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using System;
using FarseerPhysics;
using System.Diagnostics;
using System.Linq;
using Barotrauma.Extensions;
namespace Barotrauma
{
partial class GameScreen : Screen
{
private RenderTarget2D renderTargetBackground;
private RenderTarget2D renderTarget;
private RenderTarget2D renderTargetWater;
private RenderTarget2D renderTargetFinal;
private Effect damageEffect;
private Effect postProcessEffect;
private Texture2D damageStencil;
private Texture2D distortTexture;
public Effect PostProcessEffect
{
get { return postProcessEffect; }
}
public GameScreen(GraphicsDevice graphics, ContentManager content)
{
cam = new Camera();
cam.Translate(new Vector2(-10.0f, 50.0f));
CreateRenderTargets(graphics);
GameMain.Instance.OnResolutionChanged += () =>
{
CreateRenderTargets(graphics);
};
#if LINUX || OSX
//var blurEffect = content.Load<Effect>("Effects/blurshader_opengl");
damageEffect = content.Load<Effect>("Effects/damageshader_opengl");
postProcessEffect = content.Load<Effect>("Effects/postprocess_opengl");
#else
//var blurEffect = content.Load<Effect>("Effects/blurshader");
damageEffect = content.Load<Effect>("Effects/damageshader");
postProcessEffect = content.Load<Effect>("Effects/postprocess");
#endif
damageStencil = TextureLoader.FromFile("Content/Map/walldamage.png");
damageEffect.Parameters["xStencil"].SetValue(damageStencil);
damageEffect.Parameters["aMultiplier"].SetValue(50.0f);
damageEffect.Parameters["cMultiplier"].SetValue(200.0f);
distortTexture = TextureLoader.FromFile("Content/Effects/distortnormals.png");
postProcessEffect.Parameters["xDistortTexture"].SetValue(distortTexture);
}
private void CreateRenderTargets(GraphicsDevice graphics)
{
renderTarget?.Dispose();
renderTargetBackground?.Dispose();
renderTargetWater?.Dispose();
renderTargetFinal?.Dispose();
renderTarget = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight, false, SurfaceFormat.Color, DepthFormat.None, 0, RenderTargetUsage.PreserveContents);
renderTargetBackground = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
renderTargetWater = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
renderTargetFinal = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight, false, SurfaceFormat.Color, DepthFormat.None);
}
public override void AddToGUIUpdateList()
{
if (Character.Controlled != null && Character.Controlled.SelectedConstruction != null && Character.Controlled.CanInteractWith(Character.Controlled.SelectedConstruction))
{
Character.Controlled.SelectedConstruction.AddToGUIUpdateList();
}
if (GameMain.GameSession != null) GameMain.GameSession.AddToGUIUpdateList();
Character.AddAllToGUIUpdateList();
}
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
cam.UpdateTransform(true);
Submarine.CullEntities(cam);
foreach (Character c in Character.CharacterList)
{
c.AnimController.Limbs.ForEach(l => l.body.UpdateDrawPosition());
bool wasVisible = c.IsVisible;
c.DoVisibilityCheck(cam);
if (c.IsVisible != wasVisible)
{
c.AnimController.Limbs.ForEach(l => { if (l.LightSource != null) l.LightSource.Enabled = c.IsVisible; });
}
}
Stopwatch sw = new Stopwatch();
sw.Start();
DrawMap(graphics, spriteBatch, deltaTime);
sw.Stop();
GameMain.PerformanceCounter.AddElapsedTicks("DrawMap", sw.ElapsedTicks);
sw.Restart();
spriteBatch.Begin(SpriteSortMode.Deferred, null, GUI.SamplerState, null, GameMain.ScissorTestEnable);
if (Character.Controlled != null && cam != null) Character.Controlled.DrawHUD(spriteBatch, cam);
if (GameMain.GameSession != null) GameMain.GameSession.Draw(spriteBatch);
if (Character.Controlled == null && !GUI.DisableHUD)
{
for (int i = 0; i < Submarine.MainSubs.Length; i++)
{
if (Submarine.MainSubs[i] == null) continue;
if (Level.Loaded != null && Submarine.MainSubs[i].WorldPosition.Y < Level.MaxEntityDepth) continue;
Color indicatorColor = i == 0 ? Color.LightBlue * 0.5f : Color.Red * 0.5f;
GUI.DrawIndicator(
spriteBatch, Submarine.MainSubs[i].WorldPosition, cam,
Math.Max(Submarine.MainSub.Borders.Width, Submarine.MainSub.Borders.Height),
GUI.SubmarineIcon, indicatorColor);
}
}
GUI.Draw(cam, spriteBatch);
spriteBatch.End();
sw.Stop();
GameMain.PerformanceCounter.AddElapsedTicks("DrawHUD", sw.ElapsedTicks);
sw.Restart();
}
public void DrawMap(GraphicsDevice graphics, SpriteBatch spriteBatch, double deltaTime)
{
foreach (Submarine sub in Submarine.Loaded)
{
sub.UpdateTransform();
}
GameMain.ParticleManager.UpdateTransforms();
GameMain.LightManager.ObstructVision = Character.Controlled != null && Character.Controlled.ObstructVision;
if (Character.Controlled != null)
{
GameMain.LightManager.UpdateObstructVision(graphics, spriteBatch, cam, Character.Controlled.CursorWorldPosition);
}
//------------------------------------------------------------------------
graphics.SetRenderTarget(renderTarget);
graphics.Clear(Color.Transparent);
//Draw background structures and wall background sprites
//(= the background texture that's revealed when a wall is destroyed) into the background render target
//These will be visible through the LOS effect.
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
Submarine.DrawBack(spriteBatch, false, e => e is Structure s);
spriteBatch.End();
graphics.SetRenderTarget(null);
GameMain.LightManager.UpdateLightMap(graphics, spriteBatch, cam, renderTarget);
//------------------------------------------------------------------------
graphics.SetRenderTarget(renderTargetBackground);
if (Level.Loaded == null)
{
graphics.Clear(new Color(11, 18, 26, 255));
}
else
{
//graphics.Clear(new Color(255, 255, 255, 255));
Level.Loaded.DrawBack(graphics, spriteBatch, cam);
}
//draw alpha blended particles that are in water and behind subs
#if LINUX || OSX
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
#else
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
#endif
GameMain.ParticleManager.Draw(spriteBatch, true, false, Particles.ParticleBlendState.AlphaBlend);
spriteBatch.End();
//draw additive particles that are in water and behind subs
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, null, DepthStencilState.None, null, null, cam.Transform);
GameMain.ParticleManager.Draw(spriteBatch, true, false, Particles.ParticleBlendState.Additive);
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None);
spriteBatch.Draw(renderTarget, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
spriteBatch.End();
//----------------------------------------------------------------------------
//Start drawing to the normal render target (stuff that can't be seen through the LOS effect)
graphics.SetRenderTarget(renderTarget);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, null);
spriteBatch.Draw(renderTargetBackground, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
spriteBatch.End();
//Draw the rest of the structures, characters and front structures
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
Submarine.DrawBack(spriteBatch, false, s => !(s is Structure));
foreach (Character c in Character.CharacterList)
{
if (c.AnimController.Limbs.Any(l => l.DeformSprite != null) || !c.IsVisible) { continue; }
c.Draw(spriteBatch, Cam);
}
spriteBatch.End();
//draw characters with deformable limbs last, because they can't be batched into SpriteBatch
//pretty hacky way of preventing draw order issues between normal and deformable sprites
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
//backwards order to render the most recently spawned characters in front (characters spawned later have a larger sprite depth)
for (int i = Character.CharacterList.Count - 1; i >= 0; i--)
{
Character c = Character.CharacterList[i];
if (!c.IsVisible || c.AnimController.Limbs.All(l => l.DeformSprite == null)) { continue; }
c.Draw(spriteBatch, Cam);
}
spriteBatch.End();
//draw the rendertarget and particles that are only supposed to be drawn in water into renderTargetWater
graphics.SetRenderTarget(renderTargetWater);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque);
spriteBatch.Draw(renderTarget, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);// waterColor);
spriteBatch.End();
//draw alpha blended particles that are inside a sub
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.DepthRead, null, null, cam.Transform);
GameMain.ParticleManager.Draw(spriteBatch, true, true, Particles.ParticleBlendState.AlphaBlend);
spriteBatch.End();
graphics.SetRenderTarget(renderTarget);
//draw alpha blended particles that are not in water
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.DepthRead, null, null, cam.Transform);
GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.AlphaBlend);
spriteBatch.End();
//draw additive particles that are not in water
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, null, DepthStencilState.None, null, null, cam.Transform);
GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.Additive);
spriteBatch.End();
graphics.DepthStencilState = DepthStencilState.DepthRead;
graphics.SetRenderTarget(renderTargetFinal);
WaterRenderer.Instance.ResetBuffers();
Hull.UpdateVertices(graphics, cam, WaterRenderer.Instance);
WaterRenderer.Instance.RenderWater(spriteBatch, renderTargetWater, cam);
WaterRenderer.Instance.RenderAir(graphics, cam, renderTarget, Cam.ShaderTransform);
graphics.DepthStencilState = DepthStencilState.None;
spriteBatch.Begin(SpriteSortMode.Immediate,
BlendState.NonPremultiplied, SamplerState.LinearWrap,
null, null,
damageEffect,
cam.Transform);
Submarine.DrawDamageable(spriteBatch, damageEffect, false);
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
Submarine.DrawFront(spriteBatch, false, null);
spriteBatch.End();
//draw additive particles that are inside a sub
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, null, DepthStencilState.Default, null, null, cam.Transform);
GameMain.ParticleManager.Draw(spriteBatch, true, true, Particles.ParticleBlendState.Additive);
foreach (var discharger in Items.Components.ElectricalDischarger.List)
{
discharger.DrawElectricity(spriteBatch);
}
spriteBatch.End();
if (GameMain.LightManager.LightingEnabled)
{
spriteBatch.Begin(SpriteSortMode.Deferred, Lights.CustomBlendStates.Multiplicative, null, DepthStencilState.None, null, null, null);
spriteBatch.Draw(GameMain.LightManager.LightMap, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
spriteBatch.End();
}
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap, DepthStencilState.None, null, null, cam.Transform);
foreach (Character c in Character.CharacterList) c.DrawFront(spriteBatch, cam);
if (Level.Loaded != null) Level.Loaded.DrawFront(spriteBatch, cam);
if (GameMain.DebugDraw && GameMain.GameSession?.EventManager != null)
{
GameMain.GameSession.EventManager.DebugDraw(spriteBatch);
}
spriteBatch.End();
if (GameMain.LightManager.LosEnabled && GameMain.LightManager.LosMode != LosMode.None && Character.Controlled != null)
{
GameMain.LightManager.LosEffect.CurrentTechnique = GameMain.LightManager.LosEffect.Techniques["LosShader"];
GameMain.LightManager.LosEffect.Parameters["xTexture"].SetValue(renderTargetBackground);
GameMain.LightManager.LosEffect.Parameters["xLosTexture"].SetValue(GameMain.LightManager.LosTexture);
Color losColor;
if (GameMain.LightManager.LosMode == LosMode.Transparent)
{
//convert the los color to HLS and make sure the luminance of the color is always the same
//as the luminance of the ambient light color
float r = Character.Controlled?.CharacterHealth == null ?
0.0f : Math.Min(Character.Controlled.CharacterHealth.DamageOverlayTimer * 0.5f, 0.5f);
Vector3 ambientLightHls = GameMain.LightManager.AmbientLight.RgbToHLS();
Vector3 losColorHls = Color.Lerp(GameMain.LightManager.AmbientLight, Color.Red, r).RgbToHLS();
losColorHls.Y = ambientLightHls.Y;
losColor = ToolBox.HLSToRGB(losColorHls);
}
else
{
losColor = Color.Black;
}
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null, GameMain.LightManager.LosEffect, null);
spriteBatch.Draw(renderTargetBackground, new Rectangle(0, 0, spriteBatch.GraphicsDevice.Viewport.Width, spriteBatch.GraphicsDevice.Viewport.Height), losColor);
spriteBatch.End();
}
graphics.SetRenderTarget(null);
float BlurStrength = 0.0f;
float DistortStrength = 0.0f;
Vector3 chromaticAberrationStrength = GameMain.Config.ChromaticAberrationEnabled ?
new Vector3(-0.02f, -0.01f, 0.0f) : Vector3.Zero;
if (Character.Controlled != null)
{
BlurStrength = Character.Controlled.BlurStrength * 0.005f;
DistortStrength = Character.Controlled.DistortStrength;
chromaticAberrationStrength -= Vector3.One * Character.Controlled.RadialDistortStrength;
chromaticAberrationStrength += new Vector3(-0.03f, -0.015f, 0.0f) * Character.Controlled.ChromaticAberrationStrength;
}
else
{
BlurStrength = 0.0f;
DistortStrength = 0.0f;
}
string postProcessTechnique = "";
if (BlurStrength > 0.0f)
{
postProcessTechnique += "Blur";
postProcessEffect.Parameters["blurDistance"].SetValue(BlurStrength);
}
if (chromaticAberrationStrength != Vector3.Zero)
{
postProcessTechnique += "ChromaticAberration";
postProcessEffect.Parameters["chromaticAberrationStrength"].SetValue(chromaticAberrationStrength);
}
if (DistortStrength > 0.0f)
{
postProcessTechnique += "Distort";
postProcessEffect.Parameters["distortScale"].SetValue(Vector2.One * DistortStrength);
postProcessEffect.Parameters["distortUvOffset"].SetValue(WaterRenderer.Instance.WavePos * 0.001f);
#if LINUX || OSX
postProcessEffect.Parameters["xTexture"].SetValue(distortTexture);
#else
postProcessEffect.Parameters["xTexture"].SetValue(renderTargetFinal);
#endif
}
if (string.IsNullOrEmpty(postProcessTechnique))
{
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, SamplerState.PointClamp, DepthStencilState.None);
}
else
{
postProcessEffect.CurrentTechnique = postProcessEffect.Techniques[postProcessTechnique];
postProcessEffect.CurrentTechnique.Passes[0].Apply();
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, SamplerState.PointClamp, DepthStencilState.None, effect: postProcessEffect);
}
#if LINUX || OSX
spriteBatch.Draw(renderTargetFinal, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
#else
spriteBatch.Draw(DistortStrength > 0.0f ? distortTexture : renderTargetFinal, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
#endif
spriteBatch.End();
}
}
}
@@ -0,0 +1,698 @@
using Barotrauma.Lights;
using Barotrauma.RuinGeneration;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
namespace Barotrauma
{
class LevelEditorScreen : Screen
{
private readonly Camera cam;
public override Camera Cam
{
get { return cam; }
}
private GUIFrame leftPanel, rightPanel, bottomPanel, topPanel;
private LevelGenerationParams selectedParams;
private LevelObjectPrefab selectedLevelObject;
private GUIListBox paramsList, ruinParamsList, levelObjectList;
private GUIListBox editorContainer;
private GUIButton spriteEditDoneButton;
private GUITextBox seedBox;
private GUITickBox lightingEnabled, cursorLightEnabled;
private Sprite editingSprite;
private LightSource pointerLightSource;
public LevelEditorScreen()
{
cam = new Camera()
{
MinZoom = 0.01f,
MaxZoom = 1.0f
};
leftPanel = new GUIFrame(new RectTransform(new Vector2(0.125f, 0.8f), 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) })
{
Stretch = true,
RelativeSpacing = 0.01f
};
paramsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.3f), paddedLeftPanel.RectTransform));
paramsList.OnSelected += (GUIComponent component, object obj) =>
{
selectedParams = obj as LevelGenerationParams;
editorContainer.ClearChildren();
SortLevelObjectsList(selectedParams);
new SerializableEntityEditor(editorContainer.Content.RectTransform, selectedParams, false, true, elementHeight: 20);
return true;
};
ruinParamsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.2f), paddedLeftPanel.RectTransform));
ruinParamsList.OnSelected += (GUIComponent component, object obj) =>
{
var ruinGenerationParams = obj as RuinGenerationParams;
editorContainer.ClearChildren();
new SerializableEntityEditor(editorContainer.Content.RectTransform, ruinGenerationParams, false, true, elementHeight: 20);
return true;
};
new GUIButton(new RectTransform(new Vector2(1.0f, 0.05f), paddedLeftPanel.RectTransform),
TextManager.Get("leveleditor.createlevelobj"))
{
OnClicked = (btn, obj) =>
{
Wizard.Instance.Create();
return true;
}
};
lightingEnabled = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.025f), paddedLeftPanel.RectTransform),
TextManager.Get("leveleditor.lightingenabled"));
cursorLightEnabled = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.025f), paddedLeftPanel.RectTransform),
TextManager.Get("leveleditor.cursorlightenabled"));
new GUIButton(new RectTransform(new Vector2(1.0f, 0.05f), paddedLeftPanel.RectTransform),
TextManager.Get("leveleditor.reloadtextures"))
{
OnClicked = (btn, obj) =>
{
Level.Loaded?.ReloadTextures();
return true;
}
};
new GUIButton(new RectTransform(new Vector2(1.0f, 0.05f), paddedLeftPanel.RectTransform),
TextManager.Get("editor.saveall"))
{
OnClicked = (btn, obj) =>
{
SerializeAll();
return true;
}
};
rightPanel = new GUIFrame(new RectTransform(new Vector2(0.25f, 1.0f), Frame.RectTransform, Anchor.TopRight) { MinSize = new Point(450, 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) })
{
Stretch = true,
RelativeSpacing = 0.01f
};
editorContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), paddedRightPanel.RectTransform));
var seedContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), paddedRightPanel.RectTransform), isHorizontal: true);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), seedContainer.RectTransform), TextManager.Get("leveleditor.levelseed"));
seedBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), seedContainer.RectTransform), ToolBox.RandomSeed(8));
new GUIButton(new RectTransform(new Vector2(1.0f, 0.05f), paddedRightPanel.RectTransform),
TextManager.Get("leveleditor.generate"))
{
OnClicked = (btn, obj) =>
{
Submarine.Unload();
GameMain.LightManager.ClearLights();
Level.CreateRandom(seedBox.Text, generationParams: selectedParams).Generate(mirror: false);
GameMain.LightManager.AddLight(pointerLightSource);
cam.Position = new Vector2(Level.Loaded.Size.X / 2, Level.Loaded.Size.Y / 2);
foreach (GUITextBlock param in paramsList.Content.Children)
{
param.TextColor = param.UserData == selectedParams ? Color.LightGreen : param.Style.textColor;
}
seedBox.Deselect();
return true;
}
};
bottomPanel = new GUIFrame(new RectTransform(new Vector2(0.75f, 0.2f), Frame.RectTransform, Anchor.BottomLeft)
{ MaxSize = new Point(GameMain.GraphicsWidth - rightPanel.Rect.Width, 1000) }, style: "GUIFrameBottom");
levelObjectList = new GUIListBox(new RectTransform(new Vector2(0.99f, 0.85f), bottomPanel.RectTransform, Anchor.Center))
{
UseGridLayout = true
};
levelObjectList.OnSelected += (GUIComponent component, object obj) =>
{
selectedLevelObject = obj as LevelObjectPrefab;
CreateLevelObjectEditor(selectedLevelObject);
return true;
};
spriteEditDoneButton = new GUIButton(new RectTransform(new Point(200, 30), anchor: Anchor.BottomRight) { AbsoluteOffset = new Point(20, 20) },
TextManager.Get("leveleditor.spriteeditdone"))
{
OnClicked = (btn, userdata) =>
{
editingSprite = null;
return true;
}
};
topPanel = new GUIFrame(new RectTransform(new Point(400, 100), GUI.Canvas)
{ RelativeOffset = new Vector2(leftPanel.RectTransform.RelativeSize.X * 2, 0.0f) }, style: "GUIFrameTop");
}
public override void Select()
{
base.Select();
foreach (LevelObjectPrefab levelObjPrefab in LevelObjectPrefab.List)
{
foreach (Sprite sprite in levelObjPrefab.Sprites)
{
sprite?.EnsureLazyLoaded();
}
}
pointerLightSource = new LightSource(Vector2.Zero, 1000.0f, Color.White, submarine: null);
GameMain.LightManager.AddLight(pointerLightSource);
topPanel.ClearChildren();
new SerializableEntityEditor(topPanel.RectTransform, pointerLightSource.LightSourceParams, false, true);
editingSprite = null;
UpdateParamsList();
UpdateRuinParamsList();
UpdateLevelObjectsList();
}
public override void Deselect()
{
base.Deselect();
pointerLightSource?.Remove();
pointerLightSource = null;
}
private void UpdateParamsList()
{
editorContainer.ClearChildren();
paramsList.Content.ClearChildren();
foreach (LevelGenerationParams genParams in LevelGenerationParams.LevelParams)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), paramsList.Content.RectTransform) { MinSize = new Point(0, 20) },
genParams.Name)
{
Padding = Vector4.Zero,
UserData = genParams
};
}
}
private void UpdateRuinParamsList()
{
editorContainer.ClearChildren();
ruinParamsList.Content.ClearChildren();
foreach (RuinGenerationParams genParams in RuinGenerationParams.List)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), ruinParamsList.Content.RectTransform) { MinSize = new Point(0, 20) },
genParams.Name)
{
Padding = Vector4.Zero,
UserData = genParams
};
}
}
private void UpdateLevelObjectsList()
{
editorContainer.ClearChildren();
levelObjectList.Content.ClearChildren();
int objectsPerRow = (int)Math.Ceiling(levelObjectList.Content.Rect.Width / Math.Max(150 * GUI.Scale, 100));
float relWidth = 1.0f / objectsPerRow;
foreach (LevelObjectPrefab levelObjPrefab in LevelObjectPrefab.List)
{
var frame = new GUIFrame(new RectTransform(
new Vector2(relWidth, relWidth * ((float)levelObjectList.Content.Rect.Width / levelObjectList.Content.Rect.Height)),
levelObjectList.Content.RectTransform) { MinSize = new Point(0, 60) }, style: "GUITextBox")
{
UserData = levelObjPrefab
};
var paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), frame.RectTransform, Anchor.Center), style: null);
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform, Anchor.BottomCenter),
text: ToolBox.LimitString(levelObjPrefab.Name, GUI.SmallFont, paddedFrame.Rect.Width), textAlignment: Alignment.Center, font: GUI.SmallFont)
{
CanBeFocused = false,
};
Sprite sprite = levelObjPrefab.Sprites.FirstOrDefault() ?? levelObjPrefab.DeformableSprite?.Sprite;
GUIImage img = new GUIImage(new RectTransform(new Point(paddedFrame.Rect.Height, paddedFrame.Rect.Height - textBlock.Rect.Height),
paddedFrame.RectTransform, Anchor.TopCenter), sprite, scaleToFit: true)
{
CanBeFocused = false
};
}
}
private void CreateLevelObjectEditor(LevelObjectPrefab levelObjectPrefab)
{
editorContainer.ClearChildren();
var editor = new SerializableEntityEditor(editorContainer.Content.RectTransform, levelObjectPrefab, false, true, elementHeight: 20);
if (selectedParams != null)
{
var commonnessContainer = new GUILayoutGroup(new RectTransform(new Point(editor.Rect.Width, 70)), isHorizontal: false, childAnchor: Anchor.TopCenter)
{
AbsoluteSpacing = 5,
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), commonnessContainer.RectTransform),
TextManager.GetWithVariable("leveleditor.levelobjcommonness", "[leveltype]", selectedParams.Name), textAlignment: Alignment.Center);
new GUINumberInput(new RectTransform(new Vector2(0.5f, 0.4f), commonnessContainer.RectTransform), GUINumberInput.NumberType.Float)
{
MinValueFloat = 0,
MaxValueFloat = 100,
FloatValue = levelObjectPrefab.GetCommonness(selectedParams.Name),
OnValueChanged = (numberInput) =>
{
levelObjectPrefab.OverrideCommonness[selectedParams.Name] = numberInput.FloatValue;
}
};
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), commonnessContainer.RectTransform), style: null);
editor.AddCustomContent(commonnessContainer, 1);
}
Sprite sprite = levelObjectPrefab.Sprites.FirstOrDefault() ?? levelObjectPrefab.DeformableSprite?.Sprite;
if (sprite != null)
{
editor.AddCustomContent(new GUIButton(new RectTransform(new Point(editor.Rect.Width / 2, 20)),
TextManager.Get("leveleditor.editsprite"))
{
OnClicked = (btn, userdata) =>
{
editingSprite = sprite;
GameMain.SpriteEditorScreen.SelectSprite(editingSprite);
return true;
}
}, 1);
}
if (levelObjectPrefab.DeformableSprite != null)
{
var deformEditor = levelObjectPrefab.DeformableSprite.CreateEditor(editor, levelObjectPrefab.SpriteDeformations, levelObjectPrefab.Name);
deformEditor.GetChild<GUIDropDown>().OnSelected += (selected, userdata) =>
{
CreateLevelObjectEditor(selectedLevelObject);
return true;
};
editor.AddCustomContent(deformEditor, editor.ContentCount);
}
//child object editing
new GUITextBlock(new RectTransform(new Point(editor.Rect.Width, 40), editorContainer.Content.RectTransform),
TextManager.Get("leveleditor.childobjects"), textAlignment: Alignment.BottomCenter);
foreach (LevelObjectPrefab.ChildObject childObj in levelObjectPrefab.ChildObjects)
{
var childObjFrame = new GUIFrame(new RectTransform(new Point(editor.Rect.Width, 30)));
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), childObjFrame.RectTransform, Anchor.Center), isHorizontal: true)
{
Stretch = true,
RelativeSpacing = 0.05f
};
var selectedChildObj = childObj;
var dropdown = new GUIDropDown(new RectTransform(new Vector2(0.5f, 1.0f), paddedFrame.RectTransform), elementCount: 10, selectMultiple: true);
foreach (LevelObjectPrefab objPrefab in LevelObjectPrefab.List)
{
dropdown.AddItem(objPrefab.Name, objPrefab);
if (childObj.AllowedNames.Contains(objPrefab.Name)) dropdown.SelectItem(objPrefab);
}
dropdown.OnSelected = (selected, obj) =>
{
childObj.AllowedNames = dropdown.SelectedDataMultiple.Select(d => ((LevelObjectPrefab)d).Name).ToList();
return true;
};
new GUINumberInput(new RectTransform(new Vector2(0.2f, 1.0f), paddedFrame.RectTransform), GUINumberInput.NumberType.Int)
{
MinValueInt = 0,
MaxValueInt = 10,
OnValueChanged = (numberInput) =>
{
selectedChildObj.MinCount = numberInput.IntValue;
selectedChildObj.MaxCount = Math.Max(selectedChildObj.MaxCount, selectedChildObj.MinCount);
}
}.IntValue = childObj.MinCount;
new GUINumberInput(new RectTransform(new Vector2(0.2f, 1.0f), paddedFrame.RectTransform), GUINumberInput.NumberType.Int)
{
MinValueInt = 0,
MaxValueInt = 10,
OnValueChanged = (numberInput) =>
{
selectedChildObj.MaxCount = numberInput.IntValue;
selectedChildObj.MinCount = Math.Min(selectedChildObj.MaxCount, selectedChildObj.MinCount);
}
}.IntValue = childObj.MaxCount;
new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), paddedFrame.RectTransform), "X")
{
OnClicked = (btn, userdata) =>
{
selectedLevelObject.ChildObjects.Remove(selectedChildObj);
CreateLevelObjectEditor(selectedLevelObject);
return true;
}
};
childObjFrame.RectTransform.Parent = editorContainer.Content.RectTransform;
}
new GUIButton(new RectTransform(new Point(editor.Rect.Width / 2, 20), editorContainer.Content.RectTransform),
TextManager.Get("leveleditor.addchildobject"))
{
OnClicked = (btn, userdata) =>
{
selectedLevelObject.ChildObjects.Add(new LevelObjectPrefab.ChildObject());
CreateLevelObjectEditor(selectedLevelObject);
return true;
}
};
//light editing
new GUITextBlock(new RectTransform(new Point(editor.Rect.Width, 40), editorContainer.Content.RectTransform),
TextManager.Get("leveleditor.lightsources"), textAlignment: Alignment.BottomCenter);
foreach (LightSourceParams lightSourceParams in selectedLevelObject.LightSourceParams)
{
new SerializableEntityEditor(editorContainer.Content.RectTransform, lightSourceParams, inGame: false, showName: true);
}
new GUIButton(new RectTransform(new Point(editor.Rect.Width / 2, 20), editorContainer.Content.RectTransform),
TextManager.Get("leveleditor.addlightsource"))
{
OnClicked = (btn, userdata) =>
{
selectedLevelObject.LightSourceTriggerIndex.Add(-1);
selectedLevelObject.LightSourceParams.Add(new LightSourceParams(100.0f, Color.White));
CreateLevelObjectEditor(selectedLevelObject);
return true;
}
};
}
private void SortLevelObjectsList(LevelGenerationParams selectedParams)
{
//fade out levelobjects that don't spawn in this type of level
foreach (GUIComponent levelObjFrame in levelObjectList.Content.Children)
{
var levelObj = levelObjFrame.UserData as LevelObjectPrefab;
Color color = levelObj.GetCommonness(selectedParams.Name) > 0.0f ? Color.White : Color.White * 0.3f;
levelObjFrame.Color = color;
levelObjFrame.GetAnyChild<GUIImage>().Color = color;
}
//sort the levelobjects according to commonness in this level
levelObjectList.Content.RectTransform.SortChildren((c1, c2) =>
{
var levelObj1 = c1.GUIComponent.UserData as LevelObjectPrefab;
var levelObj2 = c2.GUIComponent.UserData as LevelObjectPrefab;
return Math.Sign(levelObj2.GetCommonness(selectedParams.Name) - levelObj1.GetCommonness(selectedParams.Name));
});
}
public override void AddToGUIUpdateList()
{
base.AddToGUIUpdateList();
rightPanel.Visible = leftPanel.Visible = bottomPanel.Visible = editingSprite == null;
if (editingSprite != null)
{
GameMain.SpriteEditorScreen.TopPanel.AddToGUIUpdateList();
spriteEditDoneButton.AddToGUIUpdateList();
}
else if (lightingEnabled.Selected && cursorLightEnabled.Selected)
{
topPanel.AddToGUIUpdateList();
}
}
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
if (lightingEnabled.Selected)
{
GameMain.LightManager.UpdateLightMap(graphics, spriteBatch, cam);
}
graphics.Clear(Color.Black);
if (Level.Loaded != null)
{
Level.Loaded.DrawBack(graphics, spriteBatch, cam);
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, SamplerState.LinearWrap, DepthStencilState.DepthRead, transformMatrix: cam.Transform);
Level.Loaded.DrawFront(spriteBatch, cam);
Submarine.Draw(spriteBatch, false);
Submarine.DrawFront(spriteBatch);
Submarine.DrawDamageable(spriteBatch, null);
spriteBatch.End();
if (lightingEnabled.Selected)
{
spriteBatch.Begin(SpriteSortMode.Immediate, Lights.CustomBlendStates.Multiplicative, null, DepthStencilState.None, null, null, null);
spriteBatch.Draw(GameMain.LightManager.LightMap, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
spriteBatch.End();
}
}
if (editingSprite != null)
{
GameMain.SpriteEditorScreen.Draw(deltaTime, graphics, spriteBatch);
}
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
GUI.Draw(Cam, spriteBatch);
spriteBatch.End();
}
public override void Update(double deltaTime)
{
pointerLightSource.Position = cam.ScreenToWorld(PlayerInput.MousePosition);
pointerLightSource.Enabled = cursorLightEnabled.Selected;
pointerLightSource.IsBackground = true;
cam.MoveCamera((float)deltaTime);
cam.UpdateTransform();
Level.Loaded?.Update((float)deltaTime, cam);
if (editingSprite != null)
{
GameMain.SpriteEditorScreen.Update(deltaTime);
}
}
private void SerializeAll()
{
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true,
NewLineOnAttributes = true
};
foreach (ContentFile configFile in GameMain.Instance.GetFilesOfType(ContentType.LevelGenerationParameters))
{
XDocument doc = XMLExtensions.TryLoadXml(configFile.Path);
if (doc == null) { continue; }
foreach (LevelGenerationParams genParams in LevelGenerationParams.LevelParams)
{
foreach (XElement element in doc.Root.Elements())
{
if (element.Name.ToString().ToLowerInvariant() != genParams.Name.ToLowerInvariant()) continue;
SerializableProperty.SerializeProperties(genParams, element, true);
break;
}
}
using (var writer = XmlWriter.Create(configFile.Path, settings))
{
doc.WriteTo(writer);
writer.Flush();
}
}
settings.NewLineOnAttributes = false;
foreach (ContentFile configFile in GameMain.Instance.GetFilesOfType(ContentType.LevelObjectPrefabs))
{
XDocument doc = XMLExtensions.TryLoadXml(configFile.Path);
if (doc == null) { continue; }
foreach (LevelObjectPrefab levelObjPrefab in LevelObjectPrefab.List)
{
foreach (XElement element in doc.Root.Elements())
{
if (element.Name.ToString().ToLowerInvariant() != levelObjPrefab.Name.ToLowerInvariant()) continue;
levelObjPrefab.Save(element);
break;
}
}
using (var writer = XmlWriter.Create(configFile.Path, settings))
{
doc.WriteTo(writer);
writer.Flush();
}
}
RuinGenerationParams.SaveAll();
}
private void Serialize(LevelGenerationParams genParams)
{
foreach (ContentFile configFile in GameMain.Instance.GetFilesOfType(ContentType.LevelGenerationParameters))
{
XDocument doc = XMLExtensions.TryLoadXml(configFile.Path);
if (doc == null) { continue; }
bool elementFound = false;
foreach (XElement element in doc.Root.Elements())
{
if (element.Name.ToString().ToLowerInvariant() != genParams.Name.ToLowerInvariant()) continue;
SerializableProperty.SerializeProperties(genParams, element, true);
elementFound = true;
}
if (elementFound)
{
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true,
NewLineOnAttributes = true
};
using (var writer = XmlWriter.Create(configFile.Path, settings))
{
doc.WriteTo(writer);
writer.Flush();
}
return;
}
}
}
#region LevelObject Wizard
private class Wizard
{
private LevelObjectPrefab newPrefab;
private static Wizard instance;
public static Wizard Instance
{
get
{
if (instance == null)
{
instance = new Wizard();
}
return instance;
}
}
public void AddToGUIUpdateList()
{
//activeView?.Box.AddToGUIUpdateList();
}
public GUIMessageBox Create()
{
var box = new GUIMessageBox(TextManager.Get("leveleditor.createlevelobj"), string.Empty,
new string[] { TextManager.Get("cancel"), TextManager.Get("done") }, new Vector2(0.5f, 0.8f));
box.Content.ChildAnchor = Anchor.TopCenter;
box.Content.AbsoluteSpacing = 20;
int elementSize = 30;
var listBox = new GUIListBox(new RectTransform(new Vector2(1, 0.9f), box.Content.RectTransform));
new GUITextBlock(new RectTransform(new Point(listBox.Content.Rect.Width, elementSize), listBox.Content.RectTransform),
TextManager.Get("leveleditor.levelobjname")) { CanBeFocused = false };
var nameBox = new GUITextBox(new RectTransform(new Point(listBox.Content.Rect.Width, elementSize), listBox.Content.RectTransform));
new GUITextBlock(new RectTransform(new Point(listBox.Content.Rect.Width, elementSize), listBox.Content.RectTransform),
TextManager.Get("leveleditor.levelobjtexturepath")) { CanBeFocused = false };
var texturePathBox = new GUITextBox(new RectTransform(new Point(listBox.Content.Rect.Width, elementSize), listBox.Content.RectTransform));
foreach (LevelObjectPrefab prefab in LevelObjectPrefab.List)
{
if (prefab.Sprites.FirstOrDefault() == null) continue;
texturePathBox.Text = Path.GetDirectoryName(prefab.Sprites.FirstOrDefault().FilePath);
break;
}
newPrefab = new LevelObjectPrefab(null);
new SerializableEntityEditor(listBox.Content.RectTransform, newPrefab, false, false);
box.Buttons[0].OnClicked += (b, d) =>
{
box.Close();
return true;
};
// Next
box.Buttons[1].OnClicked += (b, d) =>
{
if (string.IsNullOrEmpty(nameBox.Text))
{
nameBox.Flash(Color.Red);
GUI.AddMessage(TextManager.Get("leveleditor.levelobjnameempty"), Color.Red);
return false;
}
if (LevelObjectPrefab.List.Any(obj => obj.Name.ToLower() == nameBox.Text.ToLower()))
{
nameBox.Flash(Color.Red);
GUI.AddMessage(TextManager.Get("leveleditor.levelobjnametaken"), Color.Red);
return false;
}
if (!File.Exists(texturePathBox.Text))
{
texturePathBox.Flash(Color.Red);
GUI.AddMessage(TextManager.Get("leveleditor.levelobjtexturenotfound"), Color.Red);
return false;
}
newPrefab.Name = nameBox.Text;
XmlWriterSettings settings = new XmlWriterSettings { Indent = true };
foreach (ContentFile configFile in GameMain.Instance.GetFilesOfType(ContentType.LevelObjectPrefabs))
{
XDocument doc = XMLExtensions.TryLoadXml(configFile.Path);
if (doc == null) { continue; }
var newElement = new XElement(newPrefab.Name);
newPrefab.Save(newElement);
newElement.Add(new XElement("Sprite",
new XAttribute("texture", texturePathBox.Text),
new XAttribute("sourcerect", "0,0,100,100"),
new XAttribute("origin", "0.5,0.5")));
doc.Root.Add(newElement);
using (var writer = XmlWriter.Create(configFile.Path, settings))
{
doc.WriteTo(writer);
writer.Flush();
}
// Recreate the prefab so that the sprite loads correctly: TODO: consider a better way to do this
newPrefab = new LevelObjectPrefab(newElement);
break;
}
LevelObjectPrefab.List.Add(newPrefab);
GameMain.LevelEditorScreen.UpdateLevelObjectsList();
box.Close();
return true;
};
return box;
}
}
#endregion
}
}
@@ -0,0 +1,91 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Barotrauma
{
class LobbyScreen : Screen
{
private CampaignUI campaignUI;
private GUIFrame campaignUIContainer;
private CrewManager CrewManager
{
get { return GameMain.GameSession.CrewManager; }
}
public string GetMoney()
{
return campaignUI == null ? "" : campaignUI.GetMoney();
}
public LobbyScreen()
{
campaignUIContainer = new GUIFrame(new RectTransform(Vector2.One, Frame.RectTransform, Anchor.Center), style: null);
}
public override void Select()
{
base.Select();
CampaignMode campaign = GameMain.GameSession.GameMode as CampaignMode;
if (campaign == null) { return; }
campaign.Map.SelectLocation(-1);
campaignUIContainer.ClearChildren();
campaignUI = new CampaignUI(campaign, campaignUIContainer)
{
StartRound = StartRound,
OnLocationSelected = SelectLocation
};
campaignUI.UpdateCharacterLists();
GameAnalyticsManager.SetCustomDimension01("singleplayer");
}
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
graphics.Clear(Color.Black);
GUI.DrawBackgroundSprite(spriteBatch,
GameMain.GameSession.Map.CurrentLocation.Type.GetPortrait(GameMain.GameSession.Map.CurrentLocation.PortraitId));
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
GUI.Draw(Cam, spriteBatch);
spriteBatch.End();
}
public void SelectLocation(Location location, LocationConnection locationConnection)
{
}
private void StartRound()
{
if (GameMain.GameSession.Map.SelectedConnection == null) return;
GameMain.Instance.ShowLoading(LoadRound());
}
private IEnumerable<object> LoadRound()
{
GameMain.GameSession.StartRound(campaignUI.SelectedLevel,
reloadSub: true,
loadSecondSub: false,
mirrorLevel: GameMain.GameSession.Map.CurrentLocation != GameMain.GameSession.Map.SelectedConnection.Locations[0]);
GameMain.GameScreen.Select();
yield return CoroutineStatus.Success;
}
public bool QuitToMainMenu(GUIButton button, object selection)
{
GameMain.MainMenuScreen.Select();
return true;
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,350 @@
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Barotrauma.Particles;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using System.Xml;
using System.Text;
using Barotrauma.Extensions;
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<string, SerializableProperty> 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 readonly GUIComponent rightPanel, leftPanel;
private readonly GUIListBox prefabList;
private readonly GUITextBox filterBox;
private readonly GUITextBlock filterLabel;
private ParticlePrefab selectedPrefab;
private SerializableEntityEditor particlePrefabEditor;
private readonly Emitter emitter;
private readonly Camera cam;
public override Camera Cam
{
get
{
return cam;
}
}
public ParticleEditorScreen()
{
cam = new Camera();
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(450, 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);
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) { MinSize = new Point(20, 0) }, "x")
{
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();
particlePrefabEditor = new SerializableEntityEditor(listBox.Content.RectTransform, selectedPrefab, false, true, elementHeight: 20);
//listBox.Content.RectTransform.NonScaledSize = particlePrefabEditor.RectTransform.NonScaledSize;
//listBox.UpdateScrollBarSize();
return true;
};
}
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().ToLowerInvariant() != prefab.Name.ToLowerInvariant()) continue;
SerializableProperty.SerializeProperties(prefab, element, true);
}
}
XmlWriterSettings settings = new 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;
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true,
OmitXmlDeclaration = true,
NewLineOnAttributes = true
};
XElement element = new XElement(prefab.Name);
SerializableProperty.SerializeProperties(prefab, element, true);
StringBuilder sb = new StringBuilder();
using (var writer = XmlWriter.Create(sb, settings))
{
element.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();
}
}
}
@@ -0,0 +1,68 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
namespace Barotrauma
{
partial class Screen
{
private GUIFrame frame;
public GUIFrame Frame
{
get
{
if (frame == null)
{
frame = new GUIFrame(new RectTransform(Vector2.One, GUICanvas.Instance), style: null)
{
CanBeFocused = false
};
}
return frame;
}
}
/// <summary>
/// By default, creates a new frame for the screen and adds all elements to the gui update list.
/// </summary>
public virtual void AddToGUIUpdateList()
{
Frame.AddToGUIUpdateList();
}
public virtual void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
}
public void ColorFade(Color from, Color to, float duration)
{
if (duration <= 0.0f) return;
CoroutineManager.StartCoroutine(UpdateColorFade(from, to, duration));
}
private IEnumerable<object> UpdateColorFade(Color from, Color to, float duration)
{
while (Selected != this)
{
yield return CoroutineStatus.Running;
}
float timer = 0.0f;
while (timer < duration)
{
GUI.ScreenOverlayColor = Color.Lerp(from, to, Math.Min(timer / duration, 1.0f));
timer += CoroutineManager.UnscaledDeltaTime;
yield return CoroutineStatus.Running;
}
GUI.ScreenOverlayColor = to;
yield return CoroutineStatus.Success;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,945 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Extensions;
namespace Barotrauma
{
class SpriteEditorScreen : Screen
{
private GUIListBox textureList, spriteList;
private GUIFrame topPanel;
private GUIFrame leftPanel;
private GUIFrame rightPanel;
private GUIFrame bottomPanel;
private GUIFrame backgroundColorPanel;
private bool drawGrid, snapToGrid;
private GUIFrame topPanelContents;
private GUITextBlock texturePathText;
private GUITextBlock xmlPathText;
private GUIScrollBar zoomBar;
private List<Sprite> selectedSprites = new List<Sprite>();
private List<Sprite> dirtySprites = new List<Sprite>();
private Texture2D selectedTexture;
private Sprite lastSelected;
private Rectangle textureRect;
private float zoom = 1;
private float minZoom = 0.25f;
private float maxZoom;
private int spriteCount;
private GUITextBox filterSpritesBox;
private GUITextBlock filterSpritesLabel;
private GUITextBox filterTexturesBox;
private GUITextBlock filterTexturesLabel;
private string originLabel, positionLabel, sizeLabel;
private bool editBackgroundColor;
private Color backgroundColor = new Color(0.051f, 0.149f, 0.271f, 1.0f);
private readonly Camera cam;
public override Camera Cam
{
get { return cam; }
}
public GUIComponent TopPanel
{
get { return topPanel; }
}
public SpriteEditorScreen()
{
cam = new Camera();
CreateGUIElements();
}
#region Initialization
private void CreateGUIElements()
{
originLabel = TextManager.Get("charactereditor.origin") + ": ";
positionLabel = TextManager.GetWithVariable("charactereditor.position", "[coordinates]", string.Empty);
sizeLabel = TextManager.Get("charactereditor.origin") + ": ";
topPanel = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), Frame.RectTransform) { MinSize = new Point(0, 60) }, "GUIFrameTop");
topPanelContents = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.8f), topPanel.RectTransform, Anchor.Center), style: null);
new GUIButton(new RectTransform(new Vector2(0.12f, 0.4f), topPanelContents.RectTransform, Anchor.TopLeft)
{
RelativeOffset = new Vector2(0, 0.1f)
}, TextManager.Get("spriteeditor.reloadtexture"))
{
OnClicked = (button, userData) =>
{
if (!(textureList.SelectedData is Texture2D selectedTexture)) { return false; }
var selected = selectedSprites;
Sprite firstSelected = selected.First();
selected.ForEach(s => s.ReloadTexture());
RefreshLists();
textureList.Select(firstSelected.Texture, autoScroll: false);
selected.ForEachMod(s => spriteList.Select(s, autoScroll: false));
texturePathText.Text = TextManager.GetWithVariable("spriteeditor.texturesreloaded", "[filepath]", firstSelected.FilePath);
texturePathText.TextColor = Color.LightGreen;
return true;
}
};
new GUIButton(new RectTransform(new Vector2(0.12f, 0.4f), topPanelContents.RectTransform, Anchor.BottomLeft)
{
RelativeOffset = new Vector2(0, 0.1f)
}, TextManager.Get("spriteeditor.resetchanges"))
{
OnClicked = (button, userData) =>
{
if (selectedTexture == null) { return false; }
foreach (Sprite sprite in loadedSprites)
{
if (sprite.Texture != selectedTexture) { continue; }
var element = sprite.SourceElement;
if (element == null) { continue; }
// Not all sprites have a sourcerect defined, in which case we'll want to use the current source rect instead of an empty rect.
sprite.SourceRect = element.GetAttributeRect("sourcerect", sprite.SourceRect);
sprite.RelativeOrigin = element.GetAttributeVector2("origin", new Vector2(0.5f, 0.5f));
}
ResetWidgets();
xmlPathText.Text = TextManager.Get("spriteeditor.resetsuccessful");
xmlPathText.TextColor = Color.LightGreen;
return true;
}
};
new GUIButton(new RectTransform(new Vector2(0.12f, 0.4f), topPanelContents.RectTransform, Anchor.TopLeft)
{
RelativeOffset = new Vector2(0.15f, 0.1f)
}, TextManager.Get("spriteeditor.saveselectedsprites"))
{
OnClicked = (button, userData) =>
{
return SaveSprites(selectedSprites);
}
};
new GUIButton(new RectTransform(new Vector2(0.12f, 0.4f), topPanelContents.RectTransform, Anchor.BottomLeft)
{
RelativeOffset = new Vector2(0.15f, 0.1f)
}, TextManager.Get("spriteeditor.saveallsprites"))
{
OnClicked = (button, userData) =>
{
return SaveSprites(loadedSprites);
}
};
new GUITextBlock(new RectTransform(new Vector2(0.2f, 0.2f), topPanelContents.RectTransform, Anchor.TopCenter, Pivot.CenterRight) { RelativeOffset = new Vector2(0, 0.3f) }, TextManager.Get("spriteeditor.zoom"));
zoomBar = new GUIScrollBar(new RectTransform(new Vector2(0.2f, 0.35f), topPanelContents.RectTransform, Anchor.TopCenter, Pivot.CenterRight)
{
RelativeOffset = new Vector2(0.05f, 0.3f)
}, barSize: 0.1f)
{
BarScroll = GetBarScrollValue(),
Step = 0.01f,
OnMoved = (scrollBar, value) =>
{
zoom = MathHelper.Lerp(minZoom, maxZoom, value);
viewAreaOffset = Point.Zero;
return true;
}
};
var resetBtn = new GUIButton(new RectTransform(new Vector2(0.05f, 0.35f), topPanelContents.RectTransform, Anchor.TopCenter, Pivot.CenterLeft) { RelativeOffset = new Vector2(0.055f, 0.3f) }, TextManager.Get("spriteeditor.resetzoom"))
{
OnClicked = (box, data) =>
{
ResetZoom();
return true;
}
};
resetBtn.TextBlock.AutoScale = true;
new GUITickBox(new RectTransform(new Vector2(0.2f, 0.2f), topPanelContents.RectTransform, Anchor.BottomCenter, Pivot.CenterRight) { RelativeOffset = new Vector2(0, 0.3f) }, TextManager.Get("spriteeditor.showgrid"))
{
Selected = drawGrid,
OnSelected = (tickBox) =>
{
drawGrid = tickBox.Selected;
return true;
}
};
new GUITickBox(new RectTransform(new Vector2(0.2f, 0.2f), topPanelContents.RectTransform, Anchor.BottomCenter, Pivot.CenterRight) { RelativeOffset = new Vector2(0.17f, 0.3f) }, TextManager.Get("spriteeditor.snaptogrid"))
{
Selected = snapToGrid,
OnSelected = (tickBox) =>
{
snapToGrid = tickBox.Selected;
return true;
}
};
texturePathText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.4f), topPanelContents.RectTransform, Anchor.Center, Pivot.BottomCenter) { RelativeOffset = new Vector2(0.4f, 0) }, "", Color.LightGray);
xmlPathText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.4f), topPanelContents.RectTransform, Anchor.Center, Pivot.TopCenter) { RelativeOffset = new Vector2(0.4f, 0) }, "", Color.LightGray);
leftPanel = new GUIFrame(new RectTransform(new Vector2(0.25f, 1.0f - topPanel.RectTransform.RelativeSize.Y), Frame.RectTransform, Anchor.BottomLeft)
{ 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 };
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"
};
filterTexturesLabel = new GUITextBlock(new RectTransform(Vector2.One, filterArea.RectTransform), TextManager.Get("serverlog.filter"), font: GUI.Font) { IgnoreLayoutGroups = true }; ;
filterTexturesBox = new GUITextBox(new RectTransform(new Vector2(0.8f, 1.0f), filterArea.RectTransform), font: GUI.Font);
filterTexturesBox.OnTextChanged += (textBox, text) => { FilterTextures(text); return true; };
new GUIButton(new RectTransform(new Vector2(0.05f, 1.0f), filterArea.RectTransform) { MinSize = new Point(20, 0) }, "x")
{
OnClicked = (btn, userdata) => { FilterTextures(""); filterTexturesBox.Text = ""; filterTexturesBox.Flash(Color.White); return true; }
};
textureList = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), paddedLeftPanel.RectTransform))
{
OnSelected = (listBox, userData) =>
{
var previousTexture = selectedTexture;
selectedTexture = userData as Texture2D;
if (previousTexture != selectedTexture)
{
ResetZoom();
}
foreach (GUIComponent child in spriteList.Content.Children)
{
var textBlock = (GUITextBlock)child;
var sprite = (Sprite)textBlock.UserData;
textBlock.TextColor = new Color(textBlock.TextColor, sprite.Texture == selectedTexture ? 1.0f : 0.4f);
if (sprite.Texture == selectedTexture) { textBlock.Visible = true; }
}
if (selectedSprites.None(s => s.Texture == selectedTexture))
{
spriteList.Select(loadedSprites.First(s => s.Texture == selectedTexture), autoScroll: false);
UpdateScrollBar(spriteList);
}
texturePathText.TextColor = Color.LightGray;
topPanelContents.Visible = true;
return true;
}
};
rightPanel = new GUIFrame(new RectTransform(new Vector2(0.25f, 1.0f - topPanel.RectTransform.RelativeSize.Y), Frame.RectTransform, Anchor.BottomRight) { MinSize = new Point(150, 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) })
{
Stretch = true,
RelativeSpacing = 0.01f
};
filterArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.03f), paddedRightPanel.RectTransform) { MinSize = new Point(0, 20) }, isHorizontal: true)
{
Stretch = true,
UserData = "filterarea"
};
filterSpritesLabel = new GUITextBlock(new RectTransform(Vector2.One, filterArea.RectTransform), TextManager.Get("serverlog.filter"), font: GUI.Font) { IgnoreLayoutGroups = true };
filterSpritesBox = new GUITextBox(new RectTransform(new Vector2(0.8f, 1.0f), filterArea.RectTransform), font: GUI.Font);
filterSpritesBox.OnTextChanged += (textBox, text) => { FilterSprites(text); return true; };
new GUIButton(new RectTransform(new Vector2(0.05f, 1.0f), filterArea.RectTransform) { MinSize = new Point(20, 0) }, "x")
{
OnClicked = (btn, userdata) => { FilterSprites(""); filterSpritesBox.Text = ""; filterSpritesBox.Flash(Color.White); return true; }
};
spriteList = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), paddedRightPanel.RectTransform))
{
OnSelected = (listBox, userData) =>
{
if (!(userData is Sprite sprite)) return false;
SelectSprite(sprite);
return true;
}
};
// Background color
bottomPanel = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.05f), Frame.RectTransform, Anchor.BottomCenter), style: null, color: Color.Black * 0.5f);
new GUITickBox(new RectTransform(new Vector2(0.2f, 0.5f), bottomPanel.RectTransform, Anchor.Center), TextManager.Get("charactereditor.editbackgroundcolor"))
{
Selected = editBackgroundColor,
OnSelected = box =>
{
editBackgroundColor = box.Selected;
return true;
}
};
backgroundColorPanel = new GUIFrame(new RectTransform(new Point(400, 80), Frame.RectTransform, Anchor.BottomCenter) { RelativeOffset = new Vector2(0, 0.1f) }, style: null, color: Color.Black * 0.4f);
new GUITextBlock(new RectTransform(new Vector2(0.2f, 1), backgroundColorPanel.RectTransform) { MinSize = new Point(80, 26) }, TextManager.Get("spriteeditor.backgroundcolor"), textColor: Color.WhiteSmoke);
var inputArea = new GUILayoutGroup(new RectTransform(new Vector2(0.7f, 1), backgroundColorPanel.RectTransform, Anchor.TopRight)
{
AbsoluteOffset = new Point(20, 0)
}, isHorizontal: true, childAnchor: Anchor.CenterRight)
{
Stretch = true,
RelativeSpacing = 0.01f
};
var fields = new GUIComponent[4];
string[] colorComponentLabels = { TextManager.Get("spriteeditor.colorcomponentr"), TextManager.Get("spriteeditor.colorcomponentg"), TextManager.Get("spriteeditor.colorcomponentb") };
for (int i = 2; i >= 0; i--)
{
var element = new GUIFrame(new RectTransform(new Vector2(0.2f, 1), inputArea.RectTransform)
{
MinSize = new Point(40, 0),
MaxSize = new Point(100, 50)
}, style: null, color: Color.Black * 0.6f);
var colorLabel = new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), element.RectTransform, Anchor.CenterLeft), colorComponentLabels[i],
font: GUI.SmallFont, textAlignment: Alignment.CenterLeft);
var numberInput = new GUINumberInput(new RectTransform(new Vector2(0.7f, 1), element.RectTransform, Anchor.CenterRight), GUINumberInput.NumberType.Int)
{
Font = GUI.SmallFont
};
numberInput.MinValueInt = 0;
numberInput.MaxValueInt = 255;
numberInput.Font = GUI.SmallFont;
switch (i)
{
case 0:
colorLabel.TextColor = Color.Red;
numberInput.IntValue = backgroundColor.R;
numberInput.OnValueChanged += (numInput) => backgroundColor.R = (byte)(numInput.IntValue);
break;
case 1:
colorLabel.TextColor = Color.LightGreen;
numberInput.IntValue = backgroundColor.G;
numberInput.OnValueChanged += (numInput) => backgroundColor.G = (byte)(numInput.IntValue);
break;
case 2:
colorLabel.TextColor = Color.DeepSkyBlue;
numberInput.IntValue = backgroundColor.B;
numberInput.OnValueChanged += (numInput) => backgroundColor.B = (byte)(numInput.IntValue);
break;
}
}
}
private readonly HashSet<Sprite> loadedSprites = new HashSet<Sprite>();
private void LoadSprites()
{
loadedSprites.ForEach(s => s.Remove());
loadedSprites.Clear();
var contentPackages = GameMain.Config.SelectedContentPackages.ToList();
#if !DEBUG
var vanilla = GameMain.VanillaContent;
if (vanilla != null)
{
contentPackages.Remove(vanilla);
}
#endif
foreach (var contentPackage in contentPackages)
{
foreach (var file in contentPackage.Files)
{
if (file.Path.EndsWith(".xml"))
{
XDocument doc = XMLExtensions.TryLoadXml(file.Path);
if (doc != null)
{
LoadSprites(doc.Root);
}
}
}
}
void LoadSprites(XElement element)
{
element.Elements("sprite").ForEach(s => CreateSprite(s));
element.Elements("Sprite").ForEach(s => CreateSprite(s));
element.Elements("backgroundsprite").ForEach(s => CreateSprite(s));
element.Elements("BackgroundSprite").ForEach(s => CreateSprite(s));
element.Elements("brokensprite").ForEach(s => CreateSprite(s));
element.Elements("BrokenSprite").ForEach(s => CreateSprite(s));
element.Elements("containedsprite").ForEach(s => CreateSprite(s));
element.Elements("ContainedSprite").ForEach(s => CreateSprite(s));
element.Elements("inventoryicon").ForEach(s => CreateSprite(s));
element.Elements("InventoryIcon").ForEach(s => CreateSprite(s));
element.Elements("icon").ForEach(s => CreateSprite(s));
element.Elements("Icon").ForEach(s => CreateSprite(s));
//decorativesprites don't necessarily have textures (can be used to hide/disable other sprites)
element.Elements("decorativesprite").ForEach(s => { if (s.Attribute("texture") != null) CreateSprite(s); });
element.Elements("DecorativeSprite").ForEach(s => { if (s.Attribute("texture") != null) CreateSprite(s); });
element.Elements().ForEach(e => LoadSprites(e));
}
void CreateSprite(XElement element)
{
string spriteFolder = "";
string textureElement = element.GetAttributeString("texture", "");
// TODO: parse and create?
if (textureElement.Contains("[GENDER]") || textureElement.Contains("[HEADID]") || textureElement.Contains("[RACE]") || textureElement.Contains("[VARIANT]")) { return; }
if (!textureElement.Contains("/"))
{
var parsedPath = element.ParseContentPathFromUri();
spriteFolder = Path.GetDirectoryName(parsedPath);
}
// Uncomment if we do multiple passes -> there can be duplicates
//string identifier = Sprite.GetID(element);
//if (loadedSprites.None(s => s.ID == identifier))
//{
// loadedSprites.Add(new Sprite(element, spriteFolder));
//}
loadedSprites.Add(new Sprite(element, spriteFolder));
}
}
private bool SaveSprites(IEnumerable<Sprite> sprites)
{
if (selectedTexture == null) { return false; }
if (sprites.None()) { return false; }
HashSet<XDocument> docsToSave = new HashSet<XDocument>();
foreach (Sprite sprite in sprites)
{
if (sprite.Texture != selectedTexture) { continue; }
var element = sprite.SourceElement;
if (element == null) { continue; }
element.SetAttributeValue("sourcerect", XMLExtensions.RectToString(sprite.SourceRect));
element.SetAttributeValue("origin", XMLExtensions.Vector2ToString(sprite.RelativeOrigin));
docsToSave.Add(element.Document);
}
xmlPathText.Text = TextManager.Get("spriteeditor.allchangessavedto");
foreach (XDocument doc in docsToSave)
{
string xmlPath = doc.ParseContentPathFromUri();
xmlPathText.Text += "\n" + xmlPath;
doc.Save(xmlPath);
}
xmlPathText.TextColor = Color.LightGreen;
return true;
}
#endregion
#region Public methods
public override void AddToGUIUpdateList()
{
leftPanel.AddToGUIUpdateList();
rightPanel.AddToGUIUpdateList();
topPanel.AddToGUIUpdateList();
bottomPanel.AddToGUIUpdateList();
if (editBackgroundColor)
{
backgroundColorPanel.AddToGUIUpdateList();
}
}
public override void Update(double deltaTime)
{
base.Update(deltaTime);
Widget.EnableMultiSelect = PlayerInput.KeyDown(Keys.LeftControl);
spriteList.SelectMultiple = Widget.EnableMultiSelect;
// Select rects with the mouse
if (Widget.selectedWidgets.None() || Widget.EnableMultiSelect)
{
if (selectedTexture != null)
{
foreach (Sprite sprite in loadedSprites)
{
if (sprite.Texture != selectedTexture) continue;
if (PlayerInput.PrimaryMouseButtonClicked())
{
var scaledRect = new Rectangle(textureRect.Location + sprite.SourceRect.Location.Multiply(zoom), sprite.SourceRect.Size.Multiply(zoom));
if (scaledRect.Contains(PlayerInput.MousePosition))
{
spriteList.Select(sprite, autoScroll: false);
UpdateScrollBar(spriteList);
UpdateScrollBar(textureList);
}
}
}
}
}
if (GUI.MouseOn == null)
{
if (PlayerInput.ScrollWheelSpeed != 0)
{
zoom = MathHelper.Clamp(zoom + PlayerInput.ScrollWheelSpeed * (float)deltaTime * 0.05f * zoom, minZoom, maxZoom);
zoomBar.BarScroll = GetBarScrollValue();
}
widgets.Values.ForEach(w => w.Update((float)deltaTime));
if (PlayerInput.MidButtonHeld())
{
// "Camera" Pan
Vector2 moveSpeed = PlayerInput.MouseSpeed * (float)deltaTime * 100.0f;
viewAreaOffset += moveSpeed.ToPoint();
}
}
if (GUI.KeyboardDispatcher.Subscriber == null)
{
Point moveAmount = Point.Zero;
if (PlayerInput.KeyHit(Keys.Left)) { moveAmount.X--; }
if (PlayerInput.KeyHit(Keys.Right)) { moveAmount.X++; }
if (PlayerInput.KeyHit(Keys.Up)) { moveAmount.Y--; }
if (PlayerInput.KeyHit(Keys.Down)) { moveAmount.Y++; }
if (moveAmount != Point.Zero)
{
foreach (var sprite in selectedSprites)
{
var newRect = sprite.SourceRect;
newRect.Location += moveAmount;
UpdateSourceRect(sprite, newRect);
}
}
}
}
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
graphics.Clear(backgroundColor);
spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable, samplerState: SamplerState.PointClamp);
var viewArea = GetViewArea;
if (selectedTexture != null)
{
textureRect = new Rectangle(
(int)(viewArea.Center.X - selectedTexture.Bounds.Width / 2f * zoom),
(int)(viewArea.Center.Y - selectedTexture.Bounds.Height / 2f * zoom),
(int)(selectedTexture.Bounds.Width * zoom),
(int)(selectedTexture.Bounds.Height * zoom));
spriteBatch.Draw(selectedTexture,
viewArea.Center.ToVector2(),
sourceRectangle: null,
color: Color.White,
rotation: 0.0f,
origin: new Vector2(selectedTexture.Bounds.Width / 2.0f, selectedTexture.Bounds.Height / 2.0f),
scale: zoom,
effects: SpriteEffects.None,
layerDepth: 0);
//GUI.DrawRectangle(spriteBatch, viewArea, Color.Green, isFilled: false);
GUI.DrawRectangle(spriteBatch, textureRect, Color.Gray, isFilled: false);
if (drawGrid)
{
DrawGrid(spriteBatch, textureRect, zoom, Submarine.GridSize);
}
foreach (GUIComponent element in spriteList.Content.Children)
{
Sprite sprite = element.UserData as Sprite;
if (sprite == null) { continue; }
if (sprite.Texture != selectedTexture) continue;
spriteCount++;
Rectangle sourceRect = new Rectangle(
textureRect.X + (int)(sprite.SourceRect.X * zoom),
textureRect.Y + (int)(sprite.SourceRect.Y * zoom),
(int)(sprite.SourceRect.Width * zoom),
(int)(sprite.SourceRect.Height * zoom));
bool isSelected = selectedSprites.Contains(sprite);
GUI.DrawRectangle(spriteBatch, sourceRect, isSelected ? Color.Yellow : Color.Red * 0.5f, thickness: isSelected ? 2 : 1);
string id = sprite.ID;
if (!string.IsNullOrEmpty(id))
{
int widgetSize = 10;
Vector2 GetTopLeft() => sprite.SourceRect.Location.ToVector2();
Vector2 GetTopRight() => new Vector2(GetTopLeft().X + sprite.SourceRect.Width, GetTopLeft().Y);
Vector2 GetBottomRight() => new Vector2(GetTopRight().X, GetTopRight().Y + sprite.SourceRect.Height);
var originWidget = GetWidget($"{id}_origin", sprite, widgetSize, Widget.Shape.Cross, initMethod: w =>
{
w.tooltip = originLabel + sprite.RelativeOrigin.FormatDoubleDecimal();
w.MouseHeld += dTime =>
{
w.DrawPos = PlayerInput.MousePosition.Clamp(textureRect.Location.ToVector2() + GetTopLeft() * zoom, textureRect.Location.ToVector2() + GetBottomRight() * zoom);
sprite.Origin = (w.DrawPos - textureRect.Location.ToVector2() - sprite.SourceRect.Location.ToVector2() * zoom) / zoom;
w.tooltip = originLabel + sprite.RelativeOrigin.FormatDoubleDecimal();
};
w.refresh = () =>
w.DrawPos = (textureRect.Location.ToVector2() + (sprite.Origin + sprite.SourceRect.Location.ToVector2()) * zoom)
.Clamp(textureRect.Location.ToVector2() + GetTopLeft() * zoom, textureRect.Location.ToVector2() + GetBottomRight() * zoom);
});
var positionWidget = GetWidget($"{id}_position", sprite, widgetSize, Widget.Shape.Rectangle, initMethod: w =>
{
w.tooltip = positionLabel + sprite.SourceRect.Location;
w.MouseHeld += dTime =>
{
w.DrawPos = (drawGrid && snapToGrid) ?
SnapToGrid(PlayerInput.MousePosition, textureRect, zoom, Submarine.GridSize, Submarine.GridSize.X / 4.0f * zoom) :
PlayerInput.MousePosition;
w.DrawPos = new Vector2((float)Math.Ceiling(w.DrawPos.X), (float)Math.Ceiling(w.DrawPos.Y));
sprite.SourceRect = new Rectangle(((w.DrawPos - textureRect.Location.ToVector2()) / zoom).ToPoint(), sprite.SourceRect.Size);
if (spriteList.SelectedComponent is GUITextBlock textBox)
{
// TODO: cache the sprite name?
textBox.Text = GetSpriteName(sprite) + " " + sprite.SourceRect;
}
w.tooltip = positionLabel + sprite.SourceRect.Location;
};
w.refresh = () => w.DrawPos = textureRect.Location.ToVector2() + sprite.SourceRect.Location.ToVector2() * zoom;
});
var sizeWidget = GetWidget($"{id}_size", sprite, widgetSize, Widget.Shape.Rectangle, initMethod: w =>
{
w.tooltip = sizeLabel + sprite.SourceRect.Size;
w.MouseHeld += dTime =>
{
w.DrawPos = (drawGrid && snapToGrid) ?
SnapToGrid(PlayerInput.MousePosition, textureRect, zoom, Submarine.GridSize, Submarine.GridSize.X / 4.0f * zoom) :
PlayerInput.MousePosition;
w.DrawPos = new Vector2((float)Math.Ceiling(w.DrawPos.X), (float)Math.Ceiling(w.DrawPos.Y));
sprite.SourceRect = new Rectangle(sprite.SourceRect.Location, ((w.DrawPos - positionWidget.DrawPos) / zoom).ToPoint());
// TODO: allow to lock the origin
sprite.RelativeOrigin = sprite.RelativeOrigin;
if (spriteList.SelectedComponent is GUITextBlock textBox)
{
// TODO: cache the sprite name?
textBox.Text = GetSpriteName(sprite) + " " + sprite.SourceRect;
}
w.tooltip = sizeLabel + sprite.SourceRect.Size;
};
w.refresh = () => w.DrawPos = textureRect.Location.ToVector2() + new Vector2(sprite.SourceRect.Right, sprite.SourceRect.Bottom) * zoom;
});
if (isSelected)
{
positionWidget.Draw(spriteBatch, (float)deltaTime);
sizeWidget.Draw(spriteBatch, (float)deltaTime);
originWidget.Draw(spriteBatch, (float)deltaTime);
}
}
}
}
GUI.Draw(Cam, spriteBatch);
spriteCount = 0;
spriteBatch.End();
}
private void DrawGrid(SpriteBatch spriteBatch, Rectangle gridArea, float zoom, Vector2 gridSize)
{
gridSize *= zoom;
if (gridSize.X < 1.0f) { return; }
if (gridSize.Y < 1.0f) { return; }
int xLines = (int)(gridArea.Width / gridSize.X);
int yLines = (int)(gridArea.Height / gridSize.Y);
for (int x = 0; x <= xLines; x++)
{
GUI.DrawLine(spriteBatch,
new Vector2(gridArea.X + x * gridSize.X, gridArea.Y),
new Vector2(gridArea.X + x * gridSize.X, gridArea.Bottom),
Color.White * 0.25f);
}
for (int y = 0; y <= yLines; y++)
{
GUI.DrawLine(spriteBatch,
new Vector2(gridArea.X, gridArea.Y + y * gridSize.Y),
new Vector2(gridArea.Right, gridArea.Y + y * gridSize.Y),
Color.White * 0.25f);
}
}
private Vector2 SnapToGrid(Vector2 position, Rectangle gridArea, float zoom, Vector2 gridSize, float tolerance)
{
gridSize *= zoom;
if (gridSize.X < 1.0f) { return position; }
if (gridSize.Y < 1.0f) { return position; }
Vector2 snappedPos = position;
snappedPos.X -= gridArea.X;
snappedPos.Y -= gridArea.Y;
Vector2 gridPos = new Vector2(
MathUtils.RoundTowardsClosest(snappedPos.X, gridSize.X),
MathUtils.RoundTowardsClosest(snappedPos.Y, gridSize.Y));
if (Math.Abs(gridPos.X - snappedPos.X) < tolerance)
{
snappedPos.X = gridPos.X;
}
if (Math.Abs(gridPos.Y - snappedPos.Y) < tolerance)
{
snappedPos.Y = gridPos.Y;
}
snappedPos.X += gridArea.X;
snappedPos.Y += gridArea.Y;
return snappedPos;
}
private void FilterTextures(string text)
{
if (string.IsNullOrWhiteSpace(text))
{
filterTexturesLabel.Visible = true;
textureList.Content.Children.ForEach(c => c.Visible = true);
return;
}
text = text.ToLower();
filterTexturesLabel.Visible = false;
foreach (GUIComponent child in textureList.Content.Children)
{
if (!(child is GUITextBlock textBlock)) { continue; }
textBlock.Visible = textBlock.Text.ToLower().Contains(text);
}
}
private void FilterSprites(string text)
{
if (string.IsNullOrWhiteSpace(text))
{
filterSpritesLabel.Visible = true;
spriteList.Content.Children.ForEach(c => c.Visible = true);
return;
}
text = text.ToLower();
filterSpritesLabel.Visible = false;
foreach (GUIComponent child in spriteList.Content.Children)
{
if (!(child is GUITextBlock textBlock)) { continue; }
textBlock.Visible = textBlock.Text.ToLower().Contains(text);
}
}
public override void Select()
{
base.Select();
LoadSprites();
RefreshLists();
// Store the reference, because lastSelected is reassigned when the texture is selected.
Sprite lastSprite = lastSelected;
// Select the last selected texture if any.
// TODO: Does not work if the texture has been disposed. This happens when it's not used by any sprite -> is there a better way to identify the textures? id or something?
if (selectedTexture != null && textureList.Content.Children.Any(c => c.UserData as Texture2D == selectedTexture))
{
textureList.Select(selectedTexture, autoScroll: false);
UpdateScrollBar(textureList);
// Select the last selected sprite if any
if (lastSprite != null && spriteList.Content.Children.FirstOrDefault(c => c.UserData is Sprite s && s.ID == lastSprite.ID)?.UserData is Sprite sprite)
{
spriteList.Select(sprite, autoScroll: false);
UpdateScrollBar(spriteList);
}
}
else
{
spriteList.Select(0, autoScroll: false);
}
}
public override void Deselect()
{
base.Deselect();
loadedSprites.ForEach(s => s.Remove());
loadedSprites.Clear();
ResetWidgets();
// Automatically reload all sprites that have been selected at least once (and thus might have been edited)
var reloadedSprites = new List<Sprite>();
foreach (var sprite in dirtySprites)
{
foreach (var s in Sprite.LoadedSprites)
{
if (s.Texture == sprite.Texture && !reloadedSprites.Contains(s))
{
s.ReloadXML();
reloadedSprites.Add(s);
}
}
}
dirtySprites.Clear();
filterSpritesBox.Text = "";
filterTexturesBox.Text = "";
}
public void SelectSprite(Sprite sprite)
{
if (!loadedSprites.Contains(sprite))
{
loadedSprites.Add(sprite);
RefreshLists();
}
if (selectedSprites.Any(s => s.Texture != selectedTexture))
{
ResetWidgets();
}
if (Widget.EnableMultiSelect)
{
if (selectedSprites.Contains(sprite))
{
selectedSprites.Remove(sprite);
}
else
{
selectedSprites.Add(sprite);
dirtySprites.Add(sprite);
lastSelected = sprite;
}
}
else
{
selectedSprites.Clear();
selectedSprites.Add(sprite);
dirtySprites.Add(sprite);
lastSelected = sprite;
}
if (selectedTexture != sprite.Texture)
{
textureList.Select(sprite.Texture, autoScroll: false);
UpdateScrollBar(textureList);
}
xmlPathText.Text = string.Empty;
foreach (var s in selectedSprites)
{
texturePathText.Text = s.FilePath;
var element = s.SourceElement;
if (element != null)
{
string xmlPath = element.ParseContentPathFromUri();
if (!xmlPathText.Text.Contains(xmlPath))
{
xmlPathText.Text += "\n" + xmlPath;
}
}
}
xmlPathText.TextColor = Color.LightGray;
}
public void RefreshLists()
{
//selectedTexture = null;
selectedSprites.Clear();
textureList.ClearChildren();
spriteList.ClearChildren();
ResetWidgets();
HashSet<string> textures = new HashSet<string>();
// Create texture list
foreach (Sprite sprite in loadedSprites.OrderBy(s => Path.GetFileNameWithoutExtension(s.FilePath)))
{
//ignore sprites that don't have a file path (e.g. submarine pics)
if (string.IsNullOrEmpty(sprite.FilePath)) continue;
string normalizedFilePath = Path.GetFullPath(sprite.FilePath);
if (!textures.Contains(normalizedFilePath))
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), textureList.Content.RectTransform) { MinSize = new Point(0, 20) },
Path.GetFileName(sprite.FilePath))
{
Padding = Vector4.Zero,
ToolTip = sprite.FilePath,
UserData = sprite.Texture
};
textures.Add(normalizedFilePath);
}
}
// Create sprite list
// TODO: allow the user to choose whether to sort by file name or by texture sheet
//foreach (Sprite sprite in loadedSprites.OrderBy(s => GetSpriteName(s)))
foreach (Sprite sprite in loadedSprites.OrderBy(s => s.SourceElement.GetAttributeString("texture", string.Empty)))
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), spriteList.Content.RectTransform) { MinSize = new Point(0, 20) }, GetSpriteName(sprite) + " " + sprite.SourceRect)
{
Padding = Vector4.Zero,
UserData = sprite
};
}
topPanelContents.Visible = false;
}
public void ResetZoom()
{
if (selectedTexture == null) { return; }
var viewArea = GetViewArea;
float width = viewArea.Width / (float)selectedTexture.Width;
float height = viewArea.Height / (float)selectedTexture.Height;
maxZoom = 10; // TODO: user-definable?
zoom = Math.Min(1, Math.Min(width, height));
zoomBar.BarScroll = GetBarScrollValue();
viewAreaOffset = Point.Zero;
}
#endregion
#region Helpers
private Point viewAreaOffset;
private Rectangle GetViewArea
{
get
{
int margin = 20;
var viewArea = new Rectangle(leftPanel.Rect.Right + margin + viewAreaOffset.X, topPanel.Rect.Bottom + margin + viewAreaOffset.Y, rightPanel.Rect.Left - leftPanel.Rect.Right - margin * 2, Frame.Rect.Height - topPanel.Rect.Height - margin * 2);
return viewArea;
}
}
private float GetBarScrollValue() => MathHelper.Lerp(0, 1, MathUtils.InverseLerp(minZoom, maxZoom, zoom));
private string GetSpriteName(Sprite sprite)
{
var sourceElement = sprite.SourceElement;
if (sourceElement == null) { return string.Empty; }
string name = sprite.Name;
if (string.IsNullOrWhiteSpace(name))
{
name = sourceElement.Parent.GetAttributeString("identifier", string.Empty);
}
if (string.IsNullOrEmpty(name))
{
name = sourceElement.Parent.GetAttributeString("name", string.Empty);
}
return string.IsNullOrEmpty(name) ? Path.GetFileNameWithoutExtension(sprite.FilePath) : name;
}
private void UpdateScrollBar(GUIListBox listBox)
{
var sb = listBox.ScrollBar;
sb.BarScroll = MathHelper.Clamp(MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, listBox.Content.CountChildren - 1, listBox.SelectedIndex)), sb.MinValue, sb.MaxValue);
}
private void UpdateSourceRect(Sprite sprite, Rectangle newRect)
{
sprite.SourceRect = newRect;
// Keeps the relative origin unchanged. The absolute origin will be recalculated.
sprite.RelativeOrigin = sprite.RelativeOrigin;
}
#endregion
#region Widgets
private Dictionary<string, Widget> widgets = new Dictionary<string, Widget>();
private Widget GetWidget(string id, Sprite sprite, int size = 5, Widget.Shape shape = Widget.Shape.Rectangle, Action<Widget> initMethod = null)
{
if (!widgets.TryGetValue(id, out Widget widget))
{
int selectedSize = (int)Math.Round(size * 1.5f);
widget = new Widget(id, size, shape)
{
data = sprite,
color = Color.Yellow,
secondaryColor = Color.Gray,
tooltipOffset = new Vector2(selectedSize / 2 + 5, -10)
};
widget.PreDraw += (sp, dTime) =>
{
if (!widget.IsControlled)
{
widget.refresh();
}
};
widget.PreUpdate += dTime => widget.Enabled = selectedSprites.Contains(sprite);
widget.PostUpdate += dTime =>
{
widget.inputAreaMargin = widget.IsControlled ? 1000 : 0;
widget.size = widget.IsSelected ? selectedSize : size;
widget.isFilled = widget.IsControlled;
};
widgets.Add(id, widget);
initMethod?.Invoke(widget);
}
return widget;
}
private void ResetWidgets()
{
widgets.Clear();
Widget.selectedWidgets.Clear();
}
#endregion
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff