Release 1.10.5.0 - Autumn Update 2025
This commit is contained in:
@@ -233,7 +233,7 @@ namespace Barotrauma
|
||||
|
||||
protected virtual Color BackgroundColor => new Color(150, 150, 150);
|
||||
|
||||
private void DrawBack(SpriteBatch spriteBatch)
|
||||
protected virtual void DrawBack(SpriteBatch spriteBatch)
|
||||
{
|
||||
Color outlineColor = Color.White * 0.8f;
|
||||
Color fontColor = Color.White;
|
||||
@@ -253,9 +253,19 @@ namespace Barotrauma
|
||||
GUI.DrawRectangle(spriteBatch, HeaderRectangle, outlineColor, isFilled: false, depth: 1.0f, thickness: (int) Math.Max(1, 1.25f / camZoom));
|
||||
GUI.DrawRectangle(spriteBatch, bodyRect, outlineColor, isFilled: false, depth: 1.0f, thickness: (int) Math.Max(1, 1.25f / camZoom));
|
||||
|
||||
DrawConnections(spriteBatch);
|
||||
|
||||
Vector2 headerSize = GUIStyle.SubHeadingFont.MeasureString(Name);
|
||||
GUIStyle.SubHeadingFont.DrawString(spriteBatch, Name, HeaderRectangle.Location.ToVector2() + (HeaderRectangle.Size.ToVector2() / 2) - (headerSize / 2), fontColor);
|
||||
}
|
||||
|
||||
protected virtual void DrawConnections(SpriteBatch spriteBatch)
|
||||
{
|
||||
int x = 0, y = 0;
|
||||
foreach (EventEditorNodeConnection connection in Connections)
|
||||
{
|
||||
if (!ShouldDrawConnection(connection)) { continue; }
|
||||
|
||||
switch (connection.Type.NodeSide)
|
||||
{
|
||||
case NodeConnectionType.Side.Left:
|
||||
@@ -268,9 +278,11 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 headerSize = GUIStyle.SubHeadingFont.MeasureString(Name);
|
||||
GUIStyle.SubHeadingFont.DrawString(spriteBatch, Name, HeaderRectangle.Location.ToVector2() + (HeaderRectangle.Size.ToVector2() / 2) - (headerSize / 2), fontColor);
|
||||
protected virtual bool ShouldDrawConnection(EventEditorNodeConnection connection)
|
||||
{
|
||||
return true; // Base implementation draws all connections
|
||||
}
|
||||
|
||||
public void AddConnection(NodeConnectionType connectionType)
|
||||
@@ -337,6 +349,11 @@ namespace Barotrauma
|
||||
{
|
||||
private readonly Type type;
|
||||
|
||||
protected override Color BackgroundColor =>
|
||||
EventEditorScreen.ConversationMode && !IsInstanceOf(type, typeof(ConversationAction))
|
||||
? new Color(80, 80, 80) // Darker for non-conversation nodes in conversation mode
|
||||
: new Color(150, 150, 150); // Normal color
|
||||
|
||||
public EventNode(Type type, string name) : base(name)
|
||||
{
|
||||
this.type = type;
|
||||
@@ -387,8 +404,15 @@ namespace Barotrauma
|
||||
|
||||
Type? t = Type.GetType(element.GetAttributeString("type", string.Empty));
|
||||
if (t == null) { return null; }
|
||||
|
||||
string name = element.GetAttributeString("name", string.Empty);
|
||||
int id = element.GetAttributeInt("i", -1);
|
||||
|
||||
EventNode newNode = new EventNode(t, element.GetAttributeString("name", string.Empty)) { ID = element.GetAttributeInt("i", -1) };
|
||||
// Create the appropriate node type based on whether it's a conversation action
|
||||
EditorNode newNode = IsInstanceOf(t, typeof(ConversationAction))
|
||||
? new EventConversationNode(t, name) { ID = id }
|
||||
: new EventNode(t, name) { ID = id };
|
||||
|
||||
float posX = element.GetAttributeFloat("xpos", 0f);
|
||||
float posY = element.GetAttributeFloat("ypos", 0f);
|
||||
newNode.Position = new Vector2(posX, posY);
|
||||
|
||||
+398
@@ -0,0 +1,398 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for event nodes that display text content and can have inner Text nodes
|
||||
/// </summary>
|
||||
internal abstract class EventTextDisplayNode(Type type, string name) : EventNode(type, name)
|
||||
{
|
||||
protected virtual bool ShowOptions => false;
|
||||
|
||||
private new Rectangle HeaderRectangle
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!EventEditorScreen.ConversationMode) { return base.HeaderRectangle; }
|
||||
|
||||
Rectangle drawRect = GetDrawRectangle();
|
||||
return new Rectangle(Position.ToPoint(), new Point(drawRect.Width, 32));
|
||||
}
|
||||
}
|
||||
|
||||
public override Rectangle GetDrawRectangle()
|
||||
{
|
||||
if (!EventEditorScreen.ConversationMode) { return base.GetDrawRectangle(); }
|
||||
|
||||
var textConnection = Connections.Find(c => string.Equals(c.Attribute, "Text", StringComparison.OrdinalIgnoreCase));
|
||||
var optionConnections = ShowOptions ? Connections.Where(c => c.Type == NodeConnectionType.Option) : Enumerable.Empty<EventEditorNodeConnection>();
|
||||
|
||||
const int width = 300;
|
||||
int height = 50;
|
||||
|
||||
// Calculate height for text section
|
||||
if (textConnection != null)
|
||||
{
|
||||
string textContent = GetTextContent(textConnection);
|
||||
if (!string.IsNullOrEmpty(textContent) && GUIStyle.Font.Value != null)
|
||||
{
|
||||
string wrappedText = ToolBox.WrapText(textContent, width - 16, GUIStyle.Font.Value);
|
||||
Vector2 textSize = GUIStyle.Font.MeasureString(wrappedText);
|
||||
height += (int)textSize.Y + 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
height += 25;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate height for each option (only for conversation nodes)
|
||||
if (ShowOptions)
|
||||
{
|
||||
int optionIndex = 0;
|
||||
foreach (var option in optionConnections)
|
||||
{
|
||||
string optionText = GetOptionText(option, optionIndex);
|
||||
|
||||
if (GUIStyle.Font.Value != null)
|
||||
{
|
||||
string wrappedOption = ToolBox.WrapText(optionText, width - 40, GUIStyle.Font.Value);
|
||||
Vector2 optionSize = GUIStyle.Font.MeasureString(wrappedOption);
|
||||
height += (int)optionSize.Y + 20;
|
||||
}
|
||||
else
|
||||
{
|
||||
height += 40;
|
||||
}
|
||||
optionIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle rect = Rectangle;
|
||||
return new Rectangle(rect.X, rect.Y, width, height);
|
||||
}
|
||||
|
||||
protected override void DrawBack(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (!EventEditorScreen.ConversationMode)
|
||||
{
|
||||
base.DrawBack(spriteBatch);
|
||||
return;
|
||||
}
|
||||
|
||||
Rectangle bodyRect = GetDrawRectangle();
|
||||
|
||||
// Background colors
|
||||
Color headerColor = IsSelected ? new Color(100, 150, 200) : new Color(120, 170, 220);
|
||||
Color bodyColor = new Color(90, 120, 150);
|
||||
Color borderColor = Color.LightBlue;
|
||||
|
||||
// Draw background
|
||||
GUI.DrawRectangle(spriteBatch, HeaderRectangle, headerColor, isFilled: true, depth: 1.0f);
|
||||
GUI.DrawRectangle(spriteBatch, bodyRect, bodyColor, isFilled: true, depth: 1.0f);
|
||||
GUI.DrawRectangle(spriteBatch, HeaderRectangle, borderColor, isFilled: false, depth: 1.0f);
|
||||
GUI.DrawRectangle(spriteBatch, bodyRect, borderColor, isFilled: false, depth: 1.0f);
|
||||
|
||||
// Draw header text
|
||||
Vector2 headerSize = GUIStyle.SubHeadingFont.MeasureString(Name);
|
||||
Vector2 headerPos = HeaderRectangle.Location.ToVector2() + (HeaderRectangle.Size.ToVector2() / 2) - (headerSize / 2);
|
||||
GUIStyle.SubHeadingFont.DrawString(spriteBatch, Name, headerPos, Color.White);
|
||||
|
||||
// Draw text content
|
||||
DrawTextContent(spriteBatch, bodyRect);
|
||||
|
||||
// Let base class handle standard connections (Activate, Next, etc.)
|
||||
DrawConnections(spriteBatch);
|
||||
}
|
||||
|
||||
protected virtual void DrawTextContent(SpriteBatch spriteBatch, Rectangle bodyRect)
|
||||
{
|
||||
var textConnection = Connections.Find(c => string.Equals(c.Attribute, "Text", StringComparison.OrdinalIgnoreCase));
|
||||
var optionConnections = ShowOptions ? Connections.Where(c => c.Type == NodeConnectionType.Option) : Enumerable.Empty<EventEditorNodeConnection>();
|
||||
|
||||
Vector2 mousePos = Screen.Selected.Cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||
mousePos.Y = -mousePos.Y;
|
||||
|
||||
const int padding = 8;
|
||||
int currentY = bodyRect.Y + padding + 30;
|
||||
|
||||
// Draw text section
|
||||
if (textConnection != null)
|
||||
{
|
||||
string textContent = GetTextContent(textConnection);
|
||||
|
||||
// Wrap text and calculate height
|
||||
string wrappedText = textContent;
|
||||
int textHeight = 25;
|
||||
if (GUIStyle.Font.Value != null)
|
||||
{
|
||||
wrappedText = ToolBox.WrapText(textContent, bodyRect.Width - 24, GUIStyle.Font.Value);
|
||||
Vector2 textSize = GUIStyle.Font.MeasureString(wrappedText);
|
||||
textHeight = (int)textSize.Y + 10;
|
||||
}
|
||||
|
||||
Rectangle textRect = new Rectangle(bodyRect.X + padding, currentY, bodyRect.Width - padding * 2, textHeight);
|
||||
|
||||
// background
|
||||
GUI.DrawRectangle(spriteBatch, textRect, new Color(70, 100, 130), isFilled: true);
|
||||
GUI.DrawRectangle(spriteBatch, textRect, Color.CornflowerBlue, isFilled: false);
|
||||
|
||||
// wrapped text
|
||||
Vector2 textPos = new Vector2(textRect.X + 4, textRect.Y + 4);
|
||||
GUI.DrawString(spriteBatch, textPos, wrappedText, Color.Yellow, font: GUIStyle.Font);
|
||||
|
||||
// tooltip
|
||||
if (textRect.Contains(mousePos))
|
||||
{
|
||||
string rawTextKey = GetRawTextKey(textConnection);
|
||||
if (!string.IsNullOrEmpty(rawTextKey))
|
||||
{
|
||||
EventEditorScreen.DrawnTooltip = rawTextKey;
|
||||
}
|
||||
}
|
||||
|
||||
currentY += textHeight + 5;
|
||||
}
|
||||
|
||||
// Draw options (only for conversation nodes)
|
||||
if (ShowOptions)
|
||||
{
|
||||
DrawOptions(spriteBatch, bodyRect, optionConnections, currentY);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void DrawOptions(SpriteBatch spriteBatch, Rectangle bodyRect, IEnumerable<EventEditorNodeConnection> optionConnections, int startY)
|
||||
{
|
||||
Vector2 mousePos = Screen.Selected.Cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||
mousePos.Y = -mousePos.Y;
|
||||
const int padding = 8;
|
||||
int currentY = startY;
|
||||
|
||||
int optionIndex = 0;
|
||||
foreach (var option in optionConnections)
|
||||
{
|
||||
string optionText = GetOptionText(option, optionIndex);
|
||||
|
||||
// Wrap option text and calculate height
|
||||
string wrappedOption = optionText;
|
||||
int optionHeight = 30;
|
||||
if (GUIStyle.Font.Value != null)
|
||||
{
|
||||
wrappedOption = ToolBox.WrapText(optionText, bodyRect.Width - 40, GUIStyle.Font.Value);
|
||||
Vector2 optionSize = GUIStyle.Font.MeasureString(wrappedOption);
|
||||
optionHeight = (int)optionSize.Y + 16;
|
||||
}
|
||||
|
||||
Rectangle optionRect = new Rectangle(bodyRect.X + padding, currentY, bodyRect.Width - padding * 2, optionHeight);
|
||||
|
||||
// background - red for end conversation, blue for normal
|
||||
Color optionBg = option.EndConversation ? new Color(120, 80, 80) : new Color(80, 80, 120);
|
||||
GUI.DrawRectangle(spriteBatch, optionRect, optionBg, isFilled: true);
|
||||
GUI.DrawRectangle(spriteBatch, optionRect, Color.White, isFilled: false);
|
||||
|
||||
Vector2 optionPos = new Vector2(optionRect.X + 4, optionRect.Y + 4);
|
||||
GUI.DrawString(spriteBatch, optionPos, wrappedOption, Color.White, font: GUIStyle.Font);
|
||||
|
||||
// tooltip
|
||||
if (optionRect.Contains(mousePos))
|
||||
{
|
||||
string rawOptionKey = option.OptionText ?? "";
|
||||
if (!string.IsNullOrEmpty(rawOptionKey))
|
||||
{
|
||||
EventEditorScreen.DrawnTooltip = rawOptionKey;
|
||||
}
|
||||
}
|
||||
|
||||
// connection point
|
||||
Rectangle connRect = new Rectangle(bodyRect.Right - 1, optionRect.Y + optionHeight / 2 - 8, 16, 16);
|
||||
GUI.DrawRectangle(spriteBatch, connRect, Color.DarkGray, isFilled: true);
|
||||
GUI.DrawRectangle(spriteBatch, connRect, Color.White, isFilled: false);
|
||||
option.DrawRectangle = connRect;
|
||||
|
||||
// connection lines
|
||||
foreach (var connected in option.ConnectedTo)
|
||||
{
|
||||
Vector2 start = new Vector2(connRect.Right, connRect.Center.Y);
|
||||
Vector2 end = new Vector2(connected.DrawRectangle.Left, connected.DrawRectangle.Center.Y);
|
||||
|
||||
float knobLength = 24;
|
||||
var (points, _) = ToolBox.GetSquareLineBetweenPoints(start, end, knobLength);
|
||||
|
||||
Color lineColor = GUIStyle.Red;
|
||||
float lineWidth = Math.Max(2.0f, 2.0f / (Screen.Selected is EventEditorScreen eventEditor ? eventEditor.Cam.Zoom : 1.0f));
|
||||
|
||||
GUI.DrawLine(spriteBatch, points[0], points[1], lineColor, width: (int)lineWidth);
|
||||
GUI.DrawLine(spriteBatch, points[1], points[2], lineColor, width: (int)lineWidth);
|
||||
GUI.DrawLine(spriteBatch, points[2], points[3], lineColor, width: (int)lineWidth);
|
||||
GUI.DrawLine(spriteBatch, points[3], points[4], lineColor, width: (int)lineWidth);
|
||||
GUI.DrawLine(spriteBatch, points[4], points[5], lineColor, width: (int)lineWidth);
|
||||
}
|
||||
|
||||
currentY += optionHeight + 5;
|
||||
optionIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetOptionText(EventEditorNodeConnection option, int optionIndex)
|
||||
{
|
||||
string optionTextKey = option.OptionText ?? $"Option {optionIndex + 1}";
|
||||
var allVariants = TextManager.GetAll(optionTextKey);
|
||||
|
||||
int variantCount = allVariants.Count();
|
||||
return variantCount switch
|
||||
{
|
||||
> 1 => $"[{variantCount} variants] {string.Join(" / ", allVariants)}",
|
||||
1 => allVariants.First(),
|
||||
_ => optionTextKey
|
||||
};
|
||||
}
|
||||
|
||||
private string GetTextContent(EventEditorNodeConnection textConnection)
|
||||
{
|
||||
string textContent = "";
|
||||
|
||||
// First check if there's a direct text attribute
|
||||
if (textConnection.OverrideValue != null)
|
||||
{
|
||||
textContent = textConnection.OverrideValue.ToString() ?? "";
|
||||
}
|
||||
else
|
||||
{
|
||||
object? connectedValue = textConnection.GetValue();
|
||||
if (connectedValue != null)
|
||||
{
|
||||
textContent = connectedValue.ToString() ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
// If no direct text, check for inner Text nodes via Add connections
|
||||
if (string.IsNullOrEmpty(textContent))
|
||||
{
|
||||
var addConnection = Connections.FirstOrDefault(c => c.Type == NodeConnectionType.Add);
|
||||
if (addConnection != null && addConnection.ConnectedTo.Any())
|
||||
{
|
||||
var connectedNode = addConnection.ConnectedTo.First();
|
||||
if (connectedNode.Parent?.Name == "Text")
|
||||
{
|
||||
// Get the text content from the connected Text node
|
||||
var textNodeConnection = connectedNode.Parent.Connections.FirstOrDefault(c =>
|
||||
string.Equals(c.Attribute, "tag", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (textNodeConnection?.OverrideValue != null)
|
||||
{
|
||||
textContent = textNodeConnection.OverrideValue.ToString() ?? "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Translate the text if we found any
|
||||
if (!string.IsNullOrEmpty(textContent))
|
||||
{
|
||||
var translated = TextManager.Get(textContent);
|
||||
if (translated.Loaded)
|
||||
{
|
||||
textContent = translated.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return textContent;
|
||||
}
|
||||
|
||||
private string GetRawTextKey(EventEditorNodeConnection textConnection)
|
||||
{
|
||||
string textKey = "";
|
||||
|
||||
// First check if there's a direct text attribute
|
||||
if (textConnection.OverrideValue != null)
|
||||
{
|
||||
textKey = textConnection.OverrideValue.ToString() ?? "";
|
||||
}
|
||||
else
|
||||
{
|
||||
var connectedValue = textConnection.GetValue();
|
||||
if (connectedValue != null)
|
||||
{
|
||||
textKey = connectedValue.ToString() ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
// If no direct text, check for inner Text nodes via Add connections
|
||||
if (string.IsNullOrEmpty(textKey))
|
||||
{
|
||||
var addConnection = Connections.FirstOrDefault(c => c.Type == NodeConnectionType.Add);
|
||||
if (addConnection != null && addConnection.ConnectedTo.Any())
|
||||
{
|
||||
var connectedNode = addConnection.ConnectedTo.First();
|
||||
if (connectedNode.Parent?.Name == "Text")
|
||||
{
|
||||
// Get the text key from the connected Text node
|
||||
var textNodeConnection = connectedNode.Parent.Connections.FirstOrDefault(c =>
|
||||
string.Equals(c.Attribute, "tag", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (textNodeConnection?.OverrideValue != null)
|
||||
{
|
||||
textKey = textNodeConnection.OverrideValue.ToString() ?? "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return textKey;
|
||||
}
|
||||
|
||||
protected override bool ShouldDrawConnection(EventEditorNodeConnection connection)
|
||||
{
|
||||
if (!EventEditorScreen.ConversationMode) { return base.ShouldDrawConnection(connection); }
|
||||
|
||||
// In conversation mode, exclude Options and Text since we draw them manually
|
||||
// Also exclude Add connections since we hide the child Text nodes and display their content inline
|
||||
return connection.Type == NodeConnectionType.Activate ||
|
||||
connection.Type == NodeConnectionType.Next;
|
||||
}
|
||||
|
||||
protected override void DrawConnections(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (!EventEditorScreen.ConversationMode)
|
||||
{
|
||||
base.DrawConnections(spriteBatch);
|
||||
return;
|
||||
}
|
||||
|
||||
// In conversation mode, use the correct rectangle for connection positioning
|
||||
Rectangle correctRect = GetDrawRectangle();
|
||||
int x = 0, y = 0;
|
||||
foreach (EventEditorNodeConnection connection in Connections)
|
||||
{
|
||||
if (!ShouldDrawConnection(connection)) { continue; }
|
||||
|
||||
switch (connection.Type.NodeSide)
|
||||
{
|
||||
case NodeConnectionType.Side.Left:
|
||||
connection.Draw(spriteBatch, correctRect, y);
|
||||
y++;
|
||||
break;
|
||||
case NodeConnectionType.Side.Right:
|
||||
connection.Draw(spriteBatch, correctRect, x);
|
||||
x++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class EventConversationNode(Type type, string name) : EventTextDisplayNode(type, name)
|
||||
{
|
||||
protected override bool ShowOptions => true;
|
||||
}
|
||||
|
||||
internal class EventLogNode(Type type, string name) : EventTextDisplayNode(type, name)
|
||||
{
|
||||
protected override bool ShowOptions => false;
|
||||
}
|
||||
}
|
||||
+178
-24
@@ -18,6 +18,8 @@ namespace Barotrauma
|
||||
|
||||
public override Camera Cam { get; }
|
||||
public static string? DrawnTooltip { get; set; }
|
||||
|
||||
public static bool ConversationMode { get; set; }
|
||||
|
||||
public static readonly List<EditorNode> nodeList = new List<EditorNode>();
|
||||
|
||||
@@ -37,6 +39,11 @@ namespace Barotrauma
|
||||
private LocationType? lastTestType;
|
||||
|
||||
private GUITickBox? isTraitorEventBox;
|
||||
private GUITickBox? conversationModeBox;
|
||||
|
||||
private readonly LanguageIdentifier originalLanguage;
|
||||
|
||||
private GUIDropDown? languageDropdown;
|
||||
|
||||
private static int CreateID()
|
||||
{
|
||||
@@ -50,25 +57,99 @@ namespace Barotrauma
|
||||
{
|
||||
Cam = new Camera();
|
||||
nodeList.Clear();
|
||||
|
||||
originalLanguage = GameSettings.CurrentConfig.Language;
|
||||
|
||||
CreateGUI();
|
||||
}
|
||||
|
||||
public override void Select()
|
||||
{
|
||||
GUI.PreventPauseMenuToggle = false;
|
||||
projectName = TextManager.Get("EventEditor.Unnamed").Value;
|
||||
|
||||
UpdateLanguageDropdownSelection();
|
||||
|
||||
base.Select();
|
||||
}
|
||||
|
||||
private void UpdateLanguageDropdownSelection()
|
||||
{
|
||||
if (languageDropdown == null) { return; }
|
||||
|
||||
languageDropdown.SelectItem(GameSettings.CurrentConfig.Language);
|
||||
}
|
||||
|
||||
protected override void DeselectEditorSpecific()
|
||||
{
|
||||
// Restore the original language when leaving the editor
|
||||
var config = GameSettings.CurrentConfig;
|
||||
config.Language = originalLanguage;
|
||||
GameSettings.SetCurrentConfig(config);
|
||||
TextManager.LanguageChanged();
|
||||
}
|
||||
|
||||
private static readonly HashSet<EditorNode> hiddenNodesInConversationMode = new HashSet<EditorNode>();
|
||||
|
||||
private static bool ShouldHideNodeInConversationMode(EditorNode node)
|
||||
{
|
||||
return hiddenNodesInConversationMode.Contains(node);
|
||||
}
|
||||
|
||||
private static void UpdateHiddenNodesInConversationMode()
|
||||
{
|
||||
hiddenNodesInConversationMode.Clear();
|
||||
|
||||
// Find all text display nodes (ConversationAction and EventLogAction) and mark their inner Text nodes (and descendants) as hidden
|
||||
foreach (var textDisplayNode in nodeList.Where(IsEventTextDisplayNode))
|
||||
{
|
||||
var addConnection = textDisplayNode.Connections.FirstOrDefault(c => c.Type == NodeConnectionType.Add);
|
||||
if (addConnection != null && addConnection.ConnectedTo.Any())
|
||||
{
|
||||
foreach (var connectedNode in addConnection.ConnectedTo)
|
||||
{
|
||||
if (connectedNode.Parent?.Name == "Text")
|
||||
{
|
||||
MarkNodeAndDescendantsAsHidden(connectedNode.Parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsEventTextDisplayNode(EditorNode node) => node is EventTextDisplayNode;
|
||||
|
||||
private static void MarkNodeAndDescendantsAsHidden(EditorNode node)
|
||||
{
|
||||
hiddenNodesInConversationMode.Add(node);
|
||||
|
||||
// Recursively mark all connected child nodes as hidden
|
||||
foreach (var connection in node.Connections)
|
||||
{
|
||||
foreach (var connectedNode in connection.ConnectedTo)
|
||||
{
|
||||
if (connectedNode.Parent != null && !hiddenNodesInConversationMode.Contains(connectedNode.Parent))
|
||||
{
|
||||
MarkNodeAndDescendantsAsHidden(connectedNode.Parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateGUI()
|
||||
{
|
||||
GuiFrame = new GUIFrame(new RectTransform(new Vector2(0.2f, 0.4f), GUI.Canvas) { MinSize = new Point(300, 420) });
|
||||
GuiFrame = new GUIFrame(new RectTransform(new Vector2(0.2f, 0.5f), GUI.Canvas) { MinSize = new Point(300, 520) });
|
||||
GUILayoutGroup layoutGroup = new GUILayoutGroup(RectTransform(0.9f, 0.9f, GuiFrame, Anchor.Center)) { Stretch = true, AbsoluteSpacing = GUI.IntScale(5) };
|
||||
|
||||
// === BUTTONS === //
|
||||
GUILayoutGroup buttonLayout = new GUILayoutGroup(RectTransform(1.0f, 0.50f, layoutGroup)) { RelativeSpacing = 0.04f };
|
||||
GUIButton newProjectButton = new GUIButton(RectTransform(1.0f, 0.33f, buttonLayout), TextManager.Get("EventEditor.NewProject"));
|
||||
GUIButton saveProjectButton = new GUIButton(RectTransform(1.0f, 0.33f, buttonLayout), TextManager.Get("EventEditor.SaveProject"));
|
||||
GUIButton loadProjectButton = new GUIButton(RectTransform(1.0f, 0.33f, buttonLayout), TextManager.Get("EventEditor.LoadProject"));
|
||||
GUIButton exportProjectButton = new GUIButton(RectTransform(1.0f, 0.33f, buttonLayout), TextManager.Get("EventEditor.Export"));
|
||||
|
||||
GUILayoutGroup buttonLayout = new GUILayoutGroup(RectTransform(1.0f, 0.40f, layoutGroup)) { RelativeSpacing = 0.04f };
|
||||
GUIButton newProjectButton = new GUIButton(RectTransform(1.0f, 0.25f, buttonLayout), TextManager.Get("EventEditor.NewProject"));
|
||||
GUIButton saveProjectButton = new GUIButton(RectTransform(1.0f, 0.25f, buttonLayout), TextManager.Get("EventEditor.SaveProject"));
|
||||
GUIButton loadProjectButton = new GUIButton(RectTransform(1.0f, 0.25f, buttonLayout), TextManager.Get("EventEditor.LoadProject"));
|
||||
GUIButton exportProjectButton = new GUIButton(RectTransform(1.0f, 0.25f, buttonLayout), TextManager.Get("EventEditor.Export"));
|
||||
|
||||
// === LOAD PREFAB === //
|
||||
|
||||
GUILayoutGroup loadEventLayout = new GUILayoutGroup(RectTransform(1.0f, 0.125f, layoutGroup));
|
||||
GUILayoutGroup loadEventLayout = new GUILayoutGroup(RectTransform(1.0f, 0.10f, layoutGroup));
|
||||
new GUITextBlock(RectTransform(1.0f, 0.5f, loadEventLayout), TextManager.Get("EventEditor.LoadEvent"), font: GUIStyle.SubHeadingFont);
|
||||
|
||||
GUILayoutGroup loadDropdownLayout = new GUILayoutGroup(RectTransform(1.0f, 0.5f, loadEventLayout), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
@@ -76,8 +157,7 @@ namespace Barotrauma
|
||||
GUIButton loadButton = new GUIButton(RectTransform(0.2f, 1.0f, loadDropdownLayout), TextManager.Get("Load"));
|
||||
|
||||
// === ADD ACTION === //
|
||||
|
||||
GUILayoutGroup addActionLayout = new GUILayoutGroup(RectTransform(1.0f, 0.125f, layoutGroup));
|
||||
GUILayoutGroup addActionLayout = new GUILayoutGroup(RectTransform(1.0f, 0.10f, layoutGroup));
|
||||
new GUITextBlock(RectTransform(1.0f, 0.5f, addActionLayout), TextManager.Get("EventEditor.AddAction"), font: GUIStyle.SubHeadingFont);
|
||||
|
||||
GUILayoutGroup addActionDropdownLayout = new GUILayoutGroup(RectTransform(1.0f, 0.5f, addActionLayout), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
@@ -85,7 +165,7 @@ namespace Barotrauma
|
||||
GUIButton addActionButton = new GUIButton(RectTransform(0.2f, 1.0f, addActionDropdownLayout), TextManager.Get("EventEditor.Add"));
|
||||
|
||||
// === ADD VALUE === //
|
||||
GUILayoutGroup addValueLayout = new GUILayoutGroup(RectTransform(1.0f, 0.125f, layoutGroup));
|
||||
GUILayoutGroup addValueLayout = new GUILayoutGroup(RectTransform(1.0f, 0.10f, layoutGroup));
|
||||
new GUITextBlock(RectTransform(1.0f, 0.5f, addValueLayout), TextManager.Get("EventEditor.AddValue"), font: GUIStyle.SubHeadingFont);
|
||||
|
||||
GUILayoutGroup addValueDropdownLayout = new GUILayoutGroup(RectTransform(1.0f, 0.5f, addValueLayout), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
@@ -93,7 +173,7 @@ namespace Barotrauma
|
||||
GUIButton addValueButton = new GUIButton(RectTransform(0.2f, 1.0f, addValueDropdownLayout), TextManager.Get("EventEditor.Add"));
|
||||
|
||||
// === ADD SPECIAL === //
|
||||
GUILayoutGroup addSpecialLayout = new GUILayoutGroup(RectTransform(1.0f, 0.125f, layoutGroup));
|
||||
GUILayoutGroup addSpecialLayout = new GUILayoutGroup(RectTransform(1.0f, 0.10f, layoutGroup));
|
||||
new GUITextBlock(RectTransform(1.0f, 0.5f, addSpecialLayout), TextManager.Get("EventEditor.AddSpecial"), font: GUIStyle.SubHeadingFont);
|
||||
GUILayoutGroup addSpecialDropdownLayout = new GUILayoutGroup(RectTransform(1.0f, 0.5f, addSpecialLayout), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
GUIDropDown addSpecialDropdown = new GUIDropDown(RectTransform(0.8f, 1.0f, addSpecialDropdownLayout), elementCount: 1);
|
||||
@@ -156,7 +236,45 @@ namespace Barotrauma
|
||||
return true;
|
||||
};
|
||||
|
||||
isTraitorEventBox = new GUITickBox(RectTransform(1.0f, 0.125f, layoutGroup), "Traitor event");
|
||||
isTraitorEventBox = new GUITickBox(RectTransform(1.0f, 0.10f, layoutGroup), "Traitor event");
|
||||
|
||||
// === CONVERSATION MODE CHECKBOX === //
|
||||
conversationModeBox = new GUITickBox(RectTransform(1.0f, 0.10f, layoutGroup), "Conversation Mode");
|
||||
conversationModeBox.Selected = ConversationMode;
|
||||
conversationModeBox.OnSelected = box =>
|
||||
{
|
||||
ConversationMode = !ConversationMode;
|
||||
UpdateHiddenNodesInConversationMode();
|
||||
return true;
|
||||
};
|
||||
|
||||
// === LANGUAGE SELECTION === //
|
||||
GUILayoutGroup languageLayout = new GUILayoutGroup(RectTransform(1.0f, 0.10f, layoutGroup));
|
||||
new GUITextBlock(RectTransform(1.0f, 0.5f, languageLayout), TextManager.Get("Language"), font: GUIStyle.SubHeadingFont);
|
||||
|
||||
var languages = TextManager.AvailableLanguages
|
||||
.OrderBy(l => TextManager.GetTranslatedLanguageName(l).ToIdentifier());
|
||||
|
||||
languageDropdown = new GUIDropDown(RectTransform(1.0f, 0.5f, languageLayout), elementCount: 10);
|
||||
foreach (var language in languages)
|
||||
{
|
||||
languageDropdown.AddItem(TextManager.GetTranslatedLanguageName(language), language);
|
||||
}
|
||||
|
||||
// Select current language
|
||||
languageDropdown.SelectItem(GameSettings.CurrentConfig.Language);
|
||||
|
||||
languageDropdown.OnSelected = (component, userData) =>
|
||||
{
|
||||
if (userData is LanguageIdentifier selectedLanguage)
|
||||
{
|
||||
var config = GameSettings.CurrentConfig;
|
||||
config.Language = selectedLanguage;
|
||||
GameSettings.SetCurrentConfig(config);
|
||||
TextManager.LanguageChanged();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
}
|
||||
@@ -323,6 +441,9 @@ namespace Barotrauma
|
||||
{
|
||||
GUI.NotifyPrompt(TextManager.Get("EventEditor.RandomGenerationHeader"), TextManager.Get("EventEditor.RandomGenerationBody"));
|
||||
}
|
||||
|
||||
// Update hidden nodes after loading
|
||||
UpdateHiddenNodesInConversationMode();
|
||||
return true;
|
||||
});
|
||||
return true;
|
||||
@@ -334,7 +455,22 @@ namespace Barotrauma
|
||||
|
||||
Vector2 spawnPos = Cam.WorldViewCenter;
|
||||
spawnPos.Y = -spawnPos.Y;
|
||||
EventNode newNode = new EventNode(type, type.Name) { ID = CreateID() };
|
||||
|
||||
// Create the appropriate node type based on the action type
|
||||
EditorNode newNode;
|
||||
if (EditorNode.IsInstanceOf(type, typeof(ConversationAction)))
|
||||
{
|
||||
newNode = new EventConversationNode(type, type.Name) { ID = CreateID() };
|
||||
}
|
||||
else if (EditorNode.IsInstanceOf(type, typeof(EventLogAction)))
|
||||
{
|
||||
newNode = new EventLogNode(type, type.Name) { ID = CreateID() };
|
||||
}
|
||||
else
|
||||
{
|
||||
newNode = new EventNode(type, type.Name) { ID = CreateID() };
|
||||
}
|
||||
|
||||
newNode.Position = spawnPos - newNode.Size / 2;
|
||||
nodeList.Add(newNode);
|
||||
return true;
|
||||
@@ -399,7 +535,19 @@ namespace Barotrauma
|
||||
Type? t = Type.GetType($"Barotrauma.{subElement.Name}");
|
||||
if (t != null && EditorNode.IsInstanceOf(t, typeof(EventAction)))
|
||||
{
|
||||
newNode = new EventNode(t, subElement.Name.ToString()) { Position = new Vector2(ident, 0), ID = CreateID() };
|
||||
// Create the appropriate node type based on the action type
|
||||
if (EditorNode.IsInstanceOf(t, typeof(ConversationAction)))
|
||||
{
|
||||
newNode = new EventConversationNode(t, subElement.Name.ToString()) { Position = new Vector2(ident, 0), ID = CreateID() };
|
||||
}
|
||||
else if (EditorNode.IsInstanceOf(t, typeof(EventLogAction)))
|
||||
{
|
||||
newNode = new EventLogNode(t, subElement.Name.ToString()) { Position = new Vector2(ident, 0), ID = CreateID() };
|
||||
}
|
||||
else
|
||||
{
|
||||
newNode = new EventNode(t, subElement.Name.ToString()) { Position = new Vector2(ident, 0), ID = CreateID() };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -547,13 +695,6 @@ namespace Barotrauma
|
||||
return new RectTransform(new Vector2(x, y), parent.RectTransform, anchor);
|
||||
}
|
||||
|
||||
public override void Select()
|
||||
{
|
||||
GUI.PreventPauseMenuToggle = false;
|
||||
projectName = TextManager.Get("EventEditor.Unnamed").Value;
|
||||
base.Select();
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList()
|
||||
{
|
||||
GuiFrame.AddToGUIUpdateList();
|
||||
@@ -671,6 +812,7 @@ namespace Barotrauma
|
||||
private static void Load(XElement saveElement)
|
||||
{
|
||||
nodeList.Clear();
|
||||
hiddenNodesInConversationMode.Clear();
|
||||
projectName = saveElement.GetAttributeString("name", TextManager.Get("EventEditor.Unnamed").Value);
|
||||
foreach (XElement element in saveElement.Elements())
|
||||
{
|
||||
@@ -702,6 +844,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update hidden nodes after loading
|
||||
UpdateHiddenNodesInConversationMode();
|
||||
}
|
||||
|
||||
private static void CreateContextMenu(EditorNode node, EventEditorNodeConnection? connection = null)
|
||||
@@ -971,21 +1116,25 @@ namespace Barotrauma
|
||||
|
||||
foreach (EditorNode node in nodeList.Where(node => node is SpecialNode))
|
||||
{
|
||||
if (ConversationMode && ShouldHideNodeInConversationMode(node)) { continue; }
|
||||
node.Draw(spriteBatch);
|
||||
}
|
||||
|
||||
// Render value nodes below event nodes
|
||||
foreach (EditorNode node in nodeList.Where(node => node is ValueNode))
|
||||
{
|
||||
if (ConversationMode && ShouldHideNodeInConversationMode(node)) { continue; }
|
||||
node.Draw(spriteBatch);
|
||||
}
|
||||
|
||||
foreach (EditorNode node in nodeList.Where(node => node is EventNode))
|
||||
{
|
||||
if (ConversationMode && ShouldHideNodeInConversationMode(node)) { continue; }
|
||||
node.Draw(spriteBatch);
|
||||
}
|
||||
|
||||
|
||||
draggedNode?.Draw(spriteBatch);
|
||||
|
||||
foreach (var (node, _) in markedNodes)
|
||||
{
|
||||
node.Draw(spriteBatch);
|
||||
@@ -1013,6 +1162,11 @@ namespace Barotrauma
|
||||
CreateGUI();
|
||||
}
|
||||
|
||||
if (PlayerInput.KeyHit(Keys.R) && PlayerInput.KeyDown(Keys.LeftShift))
|
||||
{
|
||||
CreateGUI();
|
||||
}
|
||||
|
||||
Cam.MoveCamera((float) deltaTime, allowMove: true, allowZoom: GUI.MouseOn == null);
|
||||
Vector2 mousePos = Cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||
mousePos.Y = -mousePos.Y;
|
||||
|
||||
@@ -319,14 +319,21 @@ namespace Barotrauma
|
||||
graphics.Clear(Color.Transparent);
|
||||
|
||||
DamageEffect.CurrentTechnique = DamageEffect.Techniques["StencilShader"];
|
||||
DamageEffect.CurrentTechnique.Passes[0].Apply();
|
||||
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, SamplerState.LinearWrap, effect: DamageEffect, transformMatrix: cam.Transform);
|
||||
//reset so any parameters left over from previous usages of the shader don't persist
|
||||
ResetDamageEffect();
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap, effect: DamageEffect, transformMatrix: cam.Transform);
|
||||
Submarine.DrawDamageable(spriteBatch, DamageEffect, false);
|
||||
DamageEffect.Parameters["aCutoff"].SetValue(0.0f);
|
||||
DamageEffect.Parameters["cCutoff"].SetValue(0.0f);
|
||||
Submarine.DamageEffectCutoff = 0.0f;
|
||||
DamageEffect.CurrentTechnique.Passes[0].Apply();
|
||||
spriteBatch.End();
|
||||
//reset so parameters set in DrawDamageable don't persist
|
||||
ResetDamageEffect();
|
||||
|
||||
void ResetDamageEffect()
|
||||
{
|
||||
DamageEffect.Parameters["aCutoff"].SetValue(0.0f);
|
||||
DamageEffect.Parameters["cCutoff"].SetValue(0.0f);
|
||||
Submarine.DamageEffectCutoff = 0.0f;
|
||||
DamageEffect.CurrentTechnique.Passes[0].Apply();
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
GameMain.PerformanceCounter.AddElapsedTicks("Draw:Map:FrontDamageable", sw.ElapsedTicks);
|
||||
|
||||
@@ -2090,7 +2090,10 @@ namespace Barotrauma
|
||||
};
|
||||
|
||||
serverLogReverseButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.05f), serverLogListboxLayout.RectTransform), style: "UIToggleButtonVertical");
|
||||
serverLogBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.95f), serverLogListboxLayout.RectTransform));
|
||||
serverLogBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.95f), serverLogListboxLayout.RectTransform))
|
||||
{
|
||||
AutoHideScrollBar = false
|
||||
};
|
||||
|
||||
//filter tickbox list ------------------------------------------------------------------
|
||||
|
||||
@@ -2198,7 +2201,7 @@ namespace Barotrauma
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
if (GameMain.Client == null) { return true; }
|
||||
GUI.CreateVerificationPrompt(GameMain.GameSession.GameMode is CampaignMode ? "PauseMenuReturnToServerLobbyVerification" : "EndRoundSubNotAtLevelEnd",
|
||||
GUI.CreateVerificationPrompt(GameMain.GameSession?.GameMode is CampaignMode ? "PauseMenuReturnToServerLobbyVerification" : "EndRoundSubNotAtLevelEnd",
|
||||
() =>
|
||||
{
|
||||
GameMain.Client?.RequestEndRound(save: false);
|
||||
@@ -3304,11 +3307,17 @@ namespace Barotrauma
|
||||
VoteType voteType;
|
||||
if (component.Parent == GameMain.NetLobbyScreen.SubList.Content)
|
||||
{
|
||||
if (SelectedMode == GameModePreset.PvP && MultiplayerPreferences.Instance.TeamPreference is not (CharacterTeamType.Team1 or CharacterTeamType.Team2))
|
||||
if (SelectedMode == GameModePreset.PvP &&
|
||||
MultiplayerPreferences.Instance.TeamPreference is not (CharacterTeamType.Team1 or CharacterTeamType.Team2))
|
||||
{
|
||||
if (TeamPreferenceListBox == null)
|
||||
{
|
||||
//refresh player frame to ensure we create the team preference list box
|
||||
UpdatePlayerFrame(characterInfo: GameMain.Client?.CharacterInfo);
|
||||
}
|
||||
|
||||
// we are in PvP but don't have a team selected, so we can't select a sub
|
||||
// and also highlight the team selection list
|
||||
|
||||
foreach (GUIComponent child in TeamPreferenceListBox.Content.Children)
|
||||
{
|
||||
if (child.UserData is CharacterTeamType.None) { continue; }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Linq;
|
||||
@@ -42,6 +43,8 @@ namespace Barotrauma
|
||||
|
||||
protected override void Update(float deltaTime)
|
||||
{
|
||||
if (slideshowPrefab.Slides.IsEmpty) { return; }
|
||||
|
||||
var slide = slideshowPrefab.Slides[Math.Min(state, slideshowPrefab.Slides.Length - 1)];
|
||||
if (!Visible || (Finished && timer > slide.FadeOutDuration)) { return; }
|
||||
|
||||
@@ -104,6 +107,7 @@ namespace Barotrauma
|
||||
|
||||
private void RefreshText()
|
||||
{
|
||||
if (slideshowPrefab.Slides.IsEmpty) { return; }
|
||||
var slide = slideshowPrefab.Slides[Math.Min(state, slideshowPrefab.Slides.Length - 1)];
|
||||
currentText = slide.Text
|
||||
.Replace("[submarine]", Submarine.MainSub?.Info.Name ?? GameMain.GameSession?.SubmarineInfo?.Name ?? "Unknown")
|
||||
|
||||
@@ -2088,7 +2088,7 @@ namespace Barotrauma
|
||||
if (packageToSaveTo != null)
|
||||
{
|
||||
var modProject = new ModProject(packageToSaveTo);
|
||||
var fileListPath = packageToSaveTo.Path;
|
||||
string fileListPath = packageToSaveTo.Path;
|
||||
if (packageToSaveTo == ContentPackageManager.VanillaCorePackage)
|
||||
{
|
||||
#if !DEBUG
|
||||
@@ -2104,10 +2104,12 @@ namespace Barotrauma
|
||||
SubmarineType.Wreck => "Content/Map/Wrecks/{0}",
|
||||
SubmarineType.BeaconStation => "Content/Map/BeaconStations/{0}",
|
||||
SubmarineType.EnemySubmarine => "Content/Map/EnemySubmarines/{0}",
|
||||
SubmarineType.OutpostModule => MainSub.Info.FilePath.Contains("RuinModules") ? "Content/Map/RuinModules/{0}" : "Content/Map/Outposts/{0}",
|
||||
SubmarineType.OutpostModule => MainSub.Info.FilePath != null && MainSub.Info.FilePath.Contains("RuinModules") ? "Content/Map/RuinModules/{0}" : "Content/Map/Outposts/{0}",
|
||||
_ => throw new InvalidOperationException()
|
||||
}, savePath);
|
||||
modProject.ModVersion = "";
|
||||
addSubAndSave(modProject, savePath, fileListPath);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2116,27 +2118,41 @@ namespace Barotrauma
|
||||
if (existingFilePath != null)
|
||||
{
|
||||
savePath = existingFilePath;
|
||||
addSubAndSave(modProject, savePath, fileListPath);
|
||||
return true;
|
||||
}
|
||||
//otherwise make sure we're not trying to overwrite another sub in the same package
|
||||
else
|
||||
{
|
||||
savePath = Path.Combine(packageToSaveTo.Dir, savePath);
|
||||
if (File.Exists(savePath))
|
||||
var existingSubInContentPackage =
|
||||
SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Type == MainSub?.Info?.Type && packageToSaveTo.GetFiles<BaseSubFile>().Any(f => f.Path == s.FilePath));
|
||||
if (existingSubInContentPackage != null)
|
||||
{
|
||||
var verification = new GUIMessageBox(TextManager.Get("warning"), TextManager.Get("subeditor.duplicatesubinpackage"),
|
||||
new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") });
|
||||
string directoryName = Path.GetDirectoryName(existingSubInContentPackage.FilePath);
|
||||
string directoryNameRelativeToPackage = Path.GetRelativePath(Path.GetDirectoryName(packageToSaveTo.Path), directoryName);
|
||||
var verification = new GUIMessageBox(string.Empty, TextManager.GetWithVariable("subeditor.saveinexistingfolderprompt", "[folder]", directoryNameRelativeToPackage),
|
||||
[TextManager.GetWithVariable("subeditor.saveinexistingfolderprompt.yes", "[folder]", directoryNameRelativeToPackage), TextManager.Get("subeditor.saveinexistingfolderprompt.no")]);
|
||||
verification.Buttons[0].OnClicked = (_, _) =>
|
||||
{
|
||||
addSubAndSave(modProject, savePath, fileListPath);
|
||||
savePath = Path.Combine(directoryNameRelativeToPackage, savePath);
|
||||
trySaveWithDuplicateCheck(modProject, fileListPath);
|
||||
verification.Close();
|
||||
return true;
|
||||
}; verification.Buttons[1].OnClicked = (_, _) =>
|
||||
{
|
||||
trySaveWithDuplicateCheck(modProject, fileListPath);
|
||||
verification.Close();
|
||||
return true;
|
||||
};
|
||||
verification.Buttons[1].OnClicked = verification.Close;
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
trySaveWithDuplicateCheck(modProject, fileListPath);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
addSubAndSave(modProject, savePath, fileListPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2150,9 +2166,28 @@ namespace Barotrauma
|
||||
{
|
||||
ModProject modProject = new ModProject { Name = name };
|
||||
addSubAndSave(modProject, savePath, Path.Combine(Path.GetDirectoryName(savePath), ContentPackage.FileListFileName));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void trySaveWithDuplicateCheck(ModProject modProject, string fileListPath)
|
||||
{
|
||||
savePath = Path.Combine(packageToSaveTo.Dir, savePath);
|
||||
if (File.Exists(savePath))
|
||||
{
|
||||
var verification = new GUIMessageBox(TextManager.Get("warning"), TextManager.Get("subeditor.duplicatesubinpackage"),
|
||||
[TextManager.Get("yes"), TextManager.Get("no")]);
|
||||
verification.Buttons[0].OnClicked = (_, _) =>
|
||||
{
|
||||
addSubAndSave(modProject, savePath, fileListPath);
|
||||
verification.Close();
|
||||
return true;
|
||||
};
|
||||
verification.Buttons[1].OnClicked = verification.Close;
|
||||
}
|
||||
addSubAndSave(modProject, savePath, fileListPath);
|
||||
}
|
||||
|
||||
void addSubAndSave(ModProject modProject, string filePath, string packagePath)
|
||||
{
|
||||
filePath = filePath.CleanUpPath();
|
||||
@@ -2234,8 +2269,6 @@ namespace Barotrauma
|
||||
subNameLabel.Text = ToolBox.LimitString(MainSub.Info.Name, subNameLabel.Font, subNameLabel.Rect.Width);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void CreateSaveScreen(bool quickSave = false)
|
||||
@@ -2653,13 +2686,15 @@ namespace Barotrauma
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
var extraSettingsContainer = new GUILayoutGroup(new RectTransform(new Vector2(1, 0.5f), subTypeDependentSettingFrame.RectTransform))
|
||||
var extraSettingsContainer = new GUILayoutGroup(new RectTransform(new Vector2(1, 0.75f), subTypeDependentSettingFrame.RectTransform))
|
||||
{
|
||||
CanBeFocused = true,
|
||||
Visible = false,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
var extraSubInfo = GetExtraSubmarineInfo(MainSub?.Info);
|
||||
|
||||
var minDifficultyGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), extraSettingsContainer.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true
|
||||
@@ -2668,12 +2703,12 @@ namespace Barotrauma
|
||||
TextManager.Get("minleveldifficulty"), textAlignment: Alignment.CenterLeft, wrap: true);
|
||||
var numInput = new GUINumberInput(new RectTransform(new Vector2(0.4f, 1.0f), minDifficultyGroup.RectTransform), NumberType.Int)
|
||||
{
|
||||
IntValue = (int)(MainSub?.Info?.GetExtraSubmarineInfo?.MinLevelDifficulty ?? 0),
|
||||
IntValue = (int)(extraSubInfo?.MinLevelDifficulty ?? 0),
|
||||
MinValueInt = 0,
|
||||
MaxValueInt = 100,
|
||||
OnValueChanged = (numberInput) =>
|
||||
{
|
||||
MainSub.Info.GetExtraSubmarineInfo.MinLevelDifficulty = numberInput.IntValue;
|
||||
extraSubInfo.MinLevelDifficulty = numberInput.IntValue;
|
||||
}
|
||||
};
|
||||
minDifficultyGroup.RectTransform.MaxSize = numInput.TextBox.RectTransform.MaxSize;
|
||||
@@ -2685,16 +2720,17 @@ namespace Barotrauma
|
||||
TextManager.Get("maxleveldifficulty"), textAlignment: Alignment.CenterLeft, wrap: true);
|
||||
numInput = new GUINumberInput(new RectTransform(new Vector2(0.4f, 1.0f), maxDifficultyGroup.RectTransform), NumberType.Int)
|
||||
{
|
||||
IntValue = (int)(MainSub?.Info?.GetExtraSubmarineInfo?.MaxLevelDifficulty ?? 100),
|
||||
IntValue = (int)(extraSubInfo?.MaxLevelDifficulty ?? 100),
|
||||
MinValueInt = 0,
|
||||
MaxValueInt = 100,
|
||||
OnValueChanged = (numberInput) =>
|
||||
{
|
||||
MainSub.Info.GetExtraSubmarineInfo.MaxLevelDifficulty = numberInput.IntValue;
|
||||
extraSubInfo.MaxLevelDifficulty = numberInput.IntValue;
|
||||
}
|
||||
};
|
||||
maxDifficultyGroup.RectTransform.MaxSize = numInput.TextBox.RectTransform.MaxSize;
|
||||
|
||||
GUITextBox missionTagsBox = CreateMissionTagsUI(extraSettingsContainer, extraSubInfo?.MissionTags ?? Enumerable.Empty<Identifier>(), ChangeMissionTags);
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
@@ -2759,15 +2795,13 @@ namespace Barotrauma
|
||||
triggerMissionTagsGroup.RectTransform.MaxSize = triggerMissionTagsBox.RectTransform.MaxSize;
|
||||
//---------------------------------------
|
||||
|
||||
var enemySubmarineSettingsContainer = new GUILayoutGroup(new RectTransform(Vector2.One, subTypeDependentSettingFrame.RectTransform))
|
||||
var enemySubmarineSettingsContainer = new GUILayoutGroup(new RectTransform(Vector2.One, extraSettingsContainer.RectTransform))
|
||||
{
|
||||
CanBeFocused = true,
|
||||
Visible = false,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
// -------------------
|
||||
|
||||
var enemySubmarineRewardGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), enemySubmarineSettingsContainer.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true
|
||||
@@ -2802,26 +2836,6 @@ namespace Barotrauma
|
||||
}
|
||||
};
|
||||
enemySubmarineDifficultyGroup.RectTransform.MaxSize = numInput.TextBox.RectTransform.MaxSize;
|
||||
var enemySubmarineTagsGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), enemySubmarineSettingsContainer.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), enemySubmarineTagsGroup.RectTransform),
|
||||
TextManager.Get("sp.item.tags.name"), textAlignment: Alignment.CenterLeft, wrap: true);
|
||||
var tagsBox = new GUITextBox(new RectTransform(new Vector2(0.4f, 1.0f), enemySubmarineTagsGroup.RectTransform))
|
||||
{
|
||||
OnEnterPressed = ChangeEnemySubTags,
|
||||
OverflowClip = true,
|
||||
Text = "default"
|
||||
};
|
||||
tagsBox.OnDeselected += (textbox, _) => ChangeEnemySubTags(textbox, textbox.Text);
|
||||
if (MainSub?.Info?.EnemySubmarineInfo?.MissionTags != null)
|
||||
{
|
||||
tagsBox.Text = string.Join(',', MainSub.Info.EnemySubmarineInfo.MissionTags);
|
||||
}
|
||||
|
||||
enemySubmarineTagsGroup.RectTransform.MaxSize = tagsBox.RectTransform.MaxSize;
|
||||
enemySubmarineSettingsContainer.RectTransform.MinSize = new Point(0, enemySubmarineSettingsContainer.RectTransform.Children.Sum(c => c.Children.Any() ? c.Children.Max(c2 => c2.MinSize.Y) : 0));
|
||||
|
||||
//--------------------------------------------------------
|
||||
|
||||
@@ -2870,7 +2884,6 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
};
|
||||
beaconSettingsContainer.RectTransform.MinSize = new Point(0, beaconSettingsContainer.RectTransform.Children.Sum(c => c.Children.Any() ? c.Children.Max(c2 => c2.MinSize.Y) : 0));
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
||||
@@ -3110,13 +3123,26 @@ namespace Barotrauma
|
||||
{
|
||||
MainSub.Info.EnemySubmarineInfo ??= new EnemySubmarineInfo(MainSub.Info);
|
||||
}
|
||||
|
||||
// Update mission tags UI when submarine type changes
|
||||
var newExtraSubInfo = GetExtraSubmarineInfo(MainSub.Info);
|
||||
if (newExtraSubInfo != null)
|
||||
{
|
||||
missionTagsBox.Text = string.Join(',', newExtraSubInfo.MissionTags);
|
||||
}
|
||||
|
||||
previewImageButtonHolder.Children.ForEach(c => c.Enabled = MainSub.Info.AllowPreviewImage);
|
||||
outpostModuleSettingsContainer.Visible = type == SubmarineType.OutpostModule;
|
||||
extraSettingsContainer.Visible = type == SubmarineType.BeaconStation || type == SubmarineType.Wreck;
|
||||
extraSettingsContainer.Visible = newExtraSubInfo != null;
|
||||
beaconSettingsContainer.Visible = type == SubmarineType.BeaconStation;
|
||||
beaconSettingsContainer.IgnoreLayoutGroups = !beaconSettingsContainer.Visible;
|
||||
enemySubmarineSettingsContainer.Visible = type == SubmarineType.EnemySubmarine;
|
||||
enemySubmarineSettingsContainer.IgnoreLayoutGroups = !enemySubmarineSettingsContainer.Visible;
|
||||
subSettingsContainer.Visible = type == SubmarineType.Player;
|
||||
outpostSettingsContainer.Visible = type == SubmarineType.Outpost;
|
||||
|
||||
extraSettingsContainer.Recalculate();
|
||||
|
||||
return true;
|
||||
};
|
||||
subSettingsContainer.RectTransform.MinSize = new Point(0, subSettingsContainer.RectTransform.Children.Sum(c => c.Children.Any() ? c.Children.Max(c2 => c2.MinSize.Y) : 0));
|
||||
@@ -3412,6 +3438,18 @@ namespace Barotrauma
|
||||
if (quickSave) { SaveSub(packageToSaveInList.SelectedData as ContentPackage); }
|
||||
}
|
||||
|
||||
private static ExtraSubmarineInfo GetExtraSubmarineInfo(SubmarineInfo subInfo)
|
||||
{
|
||||
if (subInfo == null) { return null; }
|
||||
return subInfo.Type switch
|
||||
{
|
||||
SubmarineType.BeaconStation => subInfo.BeaconStationInfo,
|
||||
SubmarineType.Wreck => subInfo.WreckInfo,
|
||||
SubmarineType.EnemySubmarine => subInfo.EnemySubmarineInfo,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
private void CreateSaveAssemblyScreen()
|
||||
{
|
||||
SetMode(Mode.Default);
|
||||
@@ -4948,29 +4986,46 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ChangeEnemySubTags(GUITextBox textBox, string text)
|
||||
private bool ChangeMissionTags(GUITextBox textBox, string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
textBox.Flash(GUIStyle.Red);
|
||||
return false;
|
||||
}
|
||||
// Get the ExtraSubmarineInfo (all types inherit MissionTags from the parent class)
|
||||
var extraSubInfo = GetExtraSubmarineInfo(MainSub?.Info);
|
||||
|
||||
if (MainSub.Info.EnemySubmarineInfo is { } enemySubInfo)
|
||||
if (extraSubInfo?.MissionTags != null)
|
||||
{
|
||||
enemySubInfo.MissionTags.Clear();
|
||||
extraSubInfo.MissionTags.Clear();
|
||||
string[] tags = text.Split(',');
|
||||
foreach (string tag in tags)
|
||||
{
|
||||
enemySubInfo.MissionTags.Add(tag.ToIdentifier());
|
||||
extraSubInfo.MissionTags.Add(tag.ToIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
textBox.Text = text;
|
||||
textBox.Flash(GUIStyle.Green);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static GUITextBox CreateMissionTagsUI(GUIComponent parent, IEnumerable<Identifier> missionTags, GUITextBox.OnEnterHandler onEnterPressed)
|
||||
{
|
||||
var missionTagsGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), parent.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), missionTagsGroup.RectTransform),
|
||||
TextManager.Get("subeditor.missiontags"), textAlignment: Alignment.CenterLeft, wrap: true);
|
||||
var tagsBox = new GUITextBox(new RectTransform(new Vector2(0.4f, 1.0f), missionTagsGroup.RectTransform))
|
||||
{
|
||||
ToolTip = TextManager.Get("subeditor.missiontags.tooltip"),
|
||||
OnEnterPressed = onEnterPressed,
|
||||
OverflowClip = true,
|
||||
Text = missionTags != null ? string.Join(',', missionTags) : ""
|
||||
};
|
||||
tagsBox.OnDeselected += (textbox, _) => onEnterPressed(textbox, textbox.Text);
|
||||
missionTagsGroup.RectTransform.MaxSize = tagsBox.RectTransform.MaxSize;
|
||||
return tagsBox;
|
||||
}
|
||||
|
||||
private void ChangeSubDescription(GUITextBox textBox, string text)
|
||||
{
|
||||
if (MainSub != null)
|
||||
@@ -5851,7 +5906,8 @@ namespace Barotrauma
|
||||
if (entity is Item item && item.Components.Any(ic => ic is not ConnectionPanel && ic is not Repairable && ic.GuiFrame != null))
|
||||
{
|
||||
var container = item.GetComponents<ItemContainer>().ToList();
|
||||
if (!container.Any() || container.Any(ic => ic?.DrawInventory ?? false))
|
||||
if (container.None() || container.Any(ic => ic?.DrawInventory ?? false) ||
|
||||
item.GetComponent<CircuitBox>() != null)
|
||||
{
|
||||
OpenItem(item);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user