(77d1794a) Tester's build January 10th, 2020
This commit is contained in:
@@ -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
+5466
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
Reference in New Issue
Block a user