(965c31410a) Unstable v0.10.4.0

This commit is contained in:
Juan Pablo Arce
2020-07-21 08:57:50 -03:00
parent 4f8bd39789
commit 33d3a41104
546 changed files with 45952 additions and 25762 deletions
@@ -0,0 +1,662 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma
{
internal class EditorNode
{
public Vector2 Position { get; set; }
public Vector2 Size { get; set; }
public int ID;
private const int HeaderSize = 32;
public Rectangle HeaderRectangle => new Rectangle(Position.ToPoint(), new Point((int) Size.X, HeaderSize));
public Rectangle Rectangle => new Rectangle(new Point((int) Position.X, (int) Position.Y + HeaderSize), Size.ToPoint());
public string Name { get; protected set; }
public bool CanAddConnections { get; set; }
public readonly List<NodeConnection> Connections = new List<NodeConnection>();
public readonly List<NodeConnectionType> RemovableTypes = new List<NodeConnectionType>();
public bool IsHighlighted;
public bool IsSelected;
protected EditorNode(string name)
{
Name = name;
Position = Vector2.Zero;
}
public virtual XElement Save()
{
throw new NotImplementedException();
}
public XElement SaveConnections()
{
XElement allConnections = new XElement("Connections", new XAttribute("i", ID));
foreach (NodeConnection connection in Connections)
{
XElement connectionElement = new XElement("Connection");
connectionElement.Add(new XAttribute("i", connection.ID));
connectionElement.Add(new XAttribute("type", connection.Type.Label));
if (connection.EndConversation)
{
connectionElement.Add(new XAttribute("endconversation", connection.EndConversation));
}
if (!string.IsNullOrWhiteSpace(connection.OptionText))
{
connectionElement.Add(new XAttribute("optiontext", connection.OptionText));
}
if (!string.IsNullOrWhiteSpace(connection.OverrideValue?.ToString()))
{
connectionElement.Add(new XAttribute("overridevalue", connection.OverrideValue?.ToString()));
connectionElement.Add(new XAttribute("valuetype", connection.OverrideValue?.GetType().ToString()));
}
foreach (var nodeConnection in connection.ConnectedTo)
{
XElement connectedTo = new XElement("ConnectedTo",
new XAttribute("i", nodeConnection.ID),
new XAttribute("node", nodeConnection.Parent.ID));
connectionElement.Add(connectedTo);
}
allConnections.Add(connectionElement);
}
return allConnections;
}
public void LoadConnections(XElement element)
{
foreach (XElement subElement in element.Elements())
{
int id = subElement.GetAttributeInt("i", -1);
string? connectionType = subElement.GetAttributeString("type", null);
bool endConversation = subElement.GetAttributeBool("endconversation", false);
if (id < 0) { continue; }
NodeConnection? connection = Connections.Find(c => c.ID == id);
if (connection == null)
{
if (string.Equals(connectionType, NodeConnectionType.Option.Label, StringComparison.InvariantCultureIgnoreCase))
{
connection = new NodeConnection(this, NodeConnectionType.Option) { ID = id, EndConversation = endConversation };
Connections.Add(connection);
}
else
{
continue;
}
}
string? optionText = subElement.GetAttributeString("optiontext", null);
string? overrideValue = subElement.GetAttributeString("overridevalue", null);
string? valueType = subElement.GetAttributeString("valuetype", null);
if (optionText != null) { connection.OptionText = optionText; }
if (overrideValue != null && valueType != null)
{
Type? type = Type.GetType(valueType);
if (type != null)
{
if (type.IsEnum)
{
Array enums = Enum.GetValues(type);
foreach (object? @enum in enums)
{
if (string.Equals(@enum?.ToString(), overrideValue, StringComparison.InvariantCultureIgnoreCase))
{
connection.OverrideValue = @enum;
}
}
}
else
{
connection.OverrideValue = Convert.ChangeType(overrideValue, type);
}
}
}
foreach (XElement connectedTo in subElement.Elements())
{
int id2 = connectedTo.GetAttributeInt("i", -1);
int node = connectedTo.GetAttributeInt("node", -1);
if (id2 < 0 || node < 0) { continue; }
EditorNode? otherNode = EventEditorScreen.nodeList.Find(editorNode => editorNode.ID == node);
NodeConnection? otherConnection = otherNode?.Connections.Find(c => c.ID == id2);
if (otherConnection != null)
{
connection.ConnectedTo.Add(otherConnection);
}
}
}
}
public static EditorNode? Load(XElement element)
{
return element.Name.ToString().ToLowerInvariant() switch
{
"eventnode" => EventNode.LoadEventNode(element),
"valuenode" => ValueNode.LoadValueNode(element),
"customnode" => CustomNode.LoadCustomNode(element),
_ => null
};
}
public virtual XElement? ToXML()
{
XElement newElement = new XElement(Name);
foreach (var connection in Connections)
{
if (connection.Type == NodeConnectionType.Value)
{
if (connection.GetValue() != null)
{
newElement.Add(new XAttribute(connection.Attribute?.ToLowerInvariant(), connection.GetValue()));
}
}
}
newElement.Add(new XAttribute("_npos", XMLExtensions.Vector2ToString(Position)));
return newElement;
}
public void Connect(EditorNode otherNode, NodeConnectionType type)
{
NodeConnection? conn = Connections.Find(connection => connection.Type == type && !connection.ConnectedTo.Any());
NodeConnection? found = otherNode.Connections.Find(connection => connection.Type == NodeConnectionType.Activate);
if (found != null)
{
conn?.ConnectedTo.Add(found);
}
}
public void Connect(NodeConnection connection, NodeConnection ownConnection)
{
connection.ConnectedTo.Add(ownConnection);
}
public void Disconnect(NodeConnection conn)
{
foreach (var connection in EventEditorScreen.nodeList.SelectMany(editorNode => editorNode.Connections.Where(connection => connection.ConnectedTo.Contains(conn))))
{
connection.ConnectedTo.Remove(conn);
}
}
public void ClearConnections()
{
foreach (NodeConnection conn in Connections)
{
conn.ClearConnections();
}
}
public virtual Rectangle GetDrawRectangle()
{
return Rectangle;
}
public NodeConnection? GetConnectionOnMouse(Vector2 mousePos)
{
return Connections.FirstOrDefault(eventNodeConnection => eventNodeConnection.DrawRectangle.Contains(mousePos));
}
public void Draw(SpriteBatch spriteBatch)
{
DrawBack(spriteBatch);
DrawFront(spriteBatch);
}
protected virtual void DrawFront(SpriteBatch spriteBatch) { }
protected virtual Color BackgroundColor => new Color(150, 150, 150);
private void DrawBack(SpriteBatch spriteBatch)
{
Color outlineColor = Color.White * 0.8f;
Color fontColor = Color.White;
Color headerColor = IsHighlighted ? new Color(100, 100, 100) : new Color(120, 120, 120);
if (IsSelected)
{
headerColor = new Color(80, 80, 80);
}
float camZoom = Screen.Selected is EventEditorScreen eventEditor ? eventEditor.Cam.Zoom : 1.0f;
Rectangle bodyRect = GetDrawRectangle();
GUI.DrawRectangle(spriteBatch, HeaderRectangle, headerColor, isFilled: true, depth: 1.0f);
GUI.DrawRectangle(spriteBatch, bodyRect, BackgroundColor, isFilled: true, depth: 1.0f);
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));
int x = 0, y = 0;
foreach (NodeConnection connection in Connections)
{
switch (connection.Type.NodeSide)
{
case NodeConnectionType.Side.Left:
connection.Draw(spriteBatch, Rectangle, y);
y++;
break;
case NodeConnectionType.Side.Right:
connection.Draw(spriteBatch, Rectangle, x);
x++;
break;
}
}
Vector2 headerSize = GUI.SubHeadingFont.MeasureString(Name);
GUI.SubHeadingFont.DrawString(spriteBatch, Name, HeaderRectangle.Location.ToVector2() + (HeaderRectangle.Size.ToVector2() / 2) - (headerSize / 2), fontColor);
}
public virtual void AddOption()
{
Connections.Add(new NodeConnection(this, NodeConnectionType.Option));
}
public void RemoveOption(NodeConnection connection)
{
int index = Connections.IndexOf(connection);
foreach (var nodeConnection in Connections.Skip(index))
{
nodeConnection.ID--;
}
Connections.Remove(connection);
}
public EditorNode? GetNext()
{
var nextNode = Connections.Find(connection => connection.Type == NodeConnectionType.Next);
return nextNode?.ConnectedTo.FirstOrDefault()?.Parent;
}
public EditorNode? GetNext(NodeConnectionType type)
{
var nextNode = Connections.Find(connection => connection.Type == type);
return nextNode?.ConnectedTo.FirstOrDefault()?.Parent;
}
public static bool IsInstanceOf(Type type1, Type type2)
{
return type1.IsAssignableFrom(type2) || type1.IsSubclassOf(type2);
}
public EditorNode? GetParent()
{
var myNode = Connections.Find(connection => connection.Type == NodeConnectionType.Activate);
if (myNode == null) { return null; }
foreach (EditorNode editorNode in EventEditorScreen.nodeList)
{
List<NodeConnection> childConnection = editorNode.Connections.Where(connection => connection.Type == NodeConnectionType.Next ||
connection.Type == NodeConnectionType.Option ||
connection.Type == NodeConnectionType.Failure ||
connection.Type == NodeConnectionType.Success ||
connection.Type == NodeConnectionType.Add).ToList();
if (childConnection.Any(connection => connection != null && connection.ConnectedTo.Contains(myNode)))
{
return editorNode;
}
}
return null;
}
}
internal class EventNode : EditorNode
{
private readonly Type type;
public EventNode(Type type, string name) : base(name)
{
this.type = type;
Size = new Vector2(256, 256);
PropertyInfo[] properties = type.GetProperties().Where(info => info.CustomAttributes.Any(data => data.AttributeType == typeof(Serialize))).ToArray();
Connections.Add(new NodeConnection(this, NodeConnectionType.Activate));
Connections.Add(new NodeConnection(this, NodeConnectionType.Next));
foreach (PropertyInfo property in properties)
{
Connections.Add(new NodeConnection(this, NodeConnectionType.Value, property.Name, property.PropertyType, property));
}
if (IsInstanceOf(type, typeof(BinaryOptionAction)))
{
Connections.Add(new NodeConnection(this, NodeConnectionType.Success));
Connections.Add(new NodeConnection(this, NodeConnectionType.Failure));
}
if (IsInstanceOf(type, typeof(ConversationAction)))
{
CanAddConnections = true;
RemovableTypes.Add(NodeConnectionType.Option);
}
if (IsInstanceOf(type, typeof(StatusEffectAction)) || IsInstanceOf(type, typeof(MissionAction)))
{
Connections.Add(new NodeConnection(this, NodeConnectionType.Add));
}
}
public override XElement Save()
{
XElement newElement = new XElement(nameof(EventNode),
new XAttribute("i", ID),
new XAttribute("type", type.ToString()),
new XAttribute("name", Name),
new XAttribute("xpos", Position.X),
new XAttribute("ypos", Position.Y));
return newElement;
}
public static EditorNode? LoadEventNode(XElement element)
{
if (!string.Equals(element.Name.ToString(), nameof(EventNode), StringComparison.InvariantCultureIgnoreCase)) { return null; }
Type? t = Type.GetType(element.GetAttributeString("type", string.Empty));
if (t == null) { return null; }
EventNode newNode = new EventNode(t, element.GetAttributeString("name", string.Empty)) { ID = element.GetAttributeInt("i", -1) };
float posX = element.GetAttributeFloat("xpos", 0f);
float posY = element.GetAttributeFloat("ypos", 0f);
newNode.Position = new Vector2(posX, posY);
return newNode;
}
public override Rectangle GetDrawRectangle()
{
return ScaleRectFromConnections(Connections, Rectangle);
}
public static Rectangle ScaleRectFromConnections(List<NodeConnection> connections, Rectangle baseRect)
{
// determine how big this box should get based on how many input/output nodes the sides have
int y = connections.Count(connection => connection.Type.NodeSide == NodeConnectionType.Side.Left),
x = connections.Count(connection => connection.Type.NodeSide == NodeConnectionType.Side.Right);
int maxHeight = Math.Max(x, y);
Rectangle bodyRect = baseRect;
bodyRect.Height = bodyRect.Height / 8 * maxHeight;
return bodyRect;
}
public Tuple<EditorNode?, string?, bool>[] GetOptions()
{
IEnumerable<NodeConnection> myNode = Connections.Where(connection => connection.Type == NodeConnectionType.Option).ToArray();
List<Tuple<EditorNode?, string?, bool>> list = new List<Tuple<EditorNode?, string?, bool>>();
if (myNode != null)
{
foreach (NodeConnection connection in myNode)
{
if (connection.ConnectedTo.Any())
{
foreach (NodeConnection nodeConnection in connection.ConnectedTo)
{
list.Add(Tuple.Create((EditorNode?) nodeConnection.Parent, connection.OptionText, connection.EndConversation));
}
}
else
{
list.Add(Tuple.Create<EditorNode?, string?, bool>(null, connection.OptionText, connection.EndConversation));
}
}
}
return list.ToArray();
}
}
internal class ValueNode : EditorNode
{
private object? nodeValue;
public object? Value
{
get => nodeValue;
set
{
nodeValue = value;
if (value is string str)
{
WrappedText = TextManager.Get(str, true) is { } translated ? translated : str;
}
else
{
WrappedText = value?.ToString() ?? string.Empty;
}
valueTextSize = GUI.SubHeadingFont.MeasureString(WrappedText);
}
}
private Vector2 valueTextSize = Vector2.Zero;
public Type Type { get; }
public ValueNode(Type type, string name) : base(name)
{
Type = type;
Value = type.IsValueType ? Activator.CreateInstance(type) : null;
Size = new Vector2(256, 32);
Connections.Add(new NodeConnection(this, NodeConnectionType.Out, "Output", Type));
}
public override XElement Save()
{
XElement newElement = new XElement(nameof(ValueNode));
newElement.Add(new XAttribute("i", ID));
if (Value != null)
{
newElement.Add(new XAttribute("value", Value));
}
newElement.Add(new XAttribute("type", Type.ToString()));
newElement.Add(new XAttribute("name", Name));
newElement.Add(new XAttribute("xpos", Position.X));
newElement.Add(new XAttribute("ypos", Position.Y));
return newElement;
}
public override XElement? ToXML() { return null; }
public static EditorNode? LoadValueNode(XElement element)
{
if (!string.Equals(element.Name.ToString(), nameof(ValueNode), StringComparison.InvariantCultureIgnoreCase)) { return null; }
string? value = element.GetAttributeString("value", null);
Type? type = Type.GetType(element.GetAttributeString("type", string.Empty));
if (type != null)
{
ValueNode newNode = new ValueNode(type, element.GetAttributeString("name", string.Empty)) { ID = element.GetAttributeInt("i", -1) };
float posX = element.GetAttributeFloat("xpos", 0f);
float posY = element.GetAttributeFloat("ypos", 0f);
newNode.Position = new Vector2(posX, posY);
if (value != null)
{
if (type.IsEnum)
{
Array enums = Enum.GetValues(type);
foreach (object? @enum in enums)
{
if (string.Equals(@enum?.ToString(), value, StringComparison.InvariantCultureIgnoreCase))
{
newNode.Value = @enum;
}
}
}
else
{
newNode.Value = Convert.ChangeType(value, type);
}
}
return newNode;
}
return null;
}
protected override Color BackgroundColor => new Color(50, 50, 50);
private string? wrappedText;
private string? WrappedText
{
get => wrappedText;
set
{
string valueText = value ?? "null";
int width = Rectangle.Width;
if (width == 0)
{
wrappedText = valueText;
return;
}
if (width > 16)
{
width -= 16;
}
valueText = ToolBox.WrapText(valueText, width, GUI.SubHeadingFont);
wrappedText = valueText;
}
}
public override Rectangle GetDrawRectangle()
{
Rectangle drawRectangle = Rectangle;
Vector2 size = GUI.SubHeadingFont.MeasureString(WrappedText);
drawRectangle.Height = (int) Math.Max(size.Y + 16, drawRectangle.Height);
return drawRectangle;
}
protected override void DrawFront(SpriteBatch spriteBatch)
{
base.DrawFront(spriteBatch);
Vector2 pos = GetDrawRectangle().Location.ToVector2() + (GetDrawRectangle().Size.ToVector2() / 2) - (valueTextSize / 2);
Rectangle drawRect = Rectangle;
drawRect.Inflate(-1, -1);
GUI.DrawString(spriteBatch, pos, WrappedText, NodeConnection.GetPropertyColor(Type), font: GUI.SubHeadingFont);
}
}
class SpecialNode : EditorNode
{
public SpecialNode(string name) : base(name)
{
Size = new Vector2(256, 256);
}
public override Rectangle GetDrawRectangle()
{
return EventNode.ScaleRectFromConnections(Connections, Rectangle);
}
}
class CustomNode : SpecialNode
{
public CustomNode(string name) : base(name)
{
CanAddConnections = true;
RemovableTypes.Add(NodeConnectionType.Value);
Connections.Add(new NodeConnection(this, NodeConnectionType.Activate));
Connections.Add(new NodeConnection(this, NodeConnectionType.Next));
Connections.Add(new NodeConnection(this, NodeConnectionType.Add));
}
public CustomNode() : this("Custom")
{
Prompt(s =>
{
Name = s;
return true;
});
}
public override void AddOption()
{
Prompt(s =>
{
Connections.Add(new NodeConnection(this, NodeConnectionType.Value, s, typeof(string)));
return true;
});
}
public override XElement Save()
{
XElement newElement = new XElement(nameof(CustomNode));
newElement.Add(new XAttribute("i", ID));
newElement.Add(new XAttribute("name", Name));
newElement.Add(new XAttribute("xpos", Position.X));
newElement.Add(new XAttribute("ypos", Position.Y));
foreach (NodeConnection connection in Connections.FindAll(connection => connection.Type == NodeConnectionType.Value))
{
newElement.Add(new XElement("Value", new XAttribute("name", connection.Attribute)));
}
return newElement;
}
public static EditorNode? LoadCustomNode(XElement element)
{
if (!string.Equals(element.Name.ToString(), nameof(CustomNode), StringComparison.OrdinalIgnoreCase)) { return null; }
CustomNode newNode = new CustomNode(element.GetAttributeString("name", string.Empty)) { ID = element.GetAttributeInt("i", -1) };
float posX = element.GetAttributeFloat("xpos", 0f);
float posY = element.GetAttributeFloat("ypos", 0f);
newNode.Position = new Vector2(posX, posY);
foreach (XElement valueElement in element.Elements())
{
newNode.Connections.Add(new NodeConnection(newNode, NodeConnectionType.Value, valueElement.GetAttributeString("name", string.Empty), typeof(string)));
}
return newNode;
}
private static void Prompt(Func<string, bool> OnAccepted)
{
var msgBox = new GUIMessageBox(TextManager.Get("Name"), "", new[] { TextManager.Get("Ok"), TextManager.Get("Cancel") }, new Vector2(0.2f, 0.175f), minSize: new Point(300, 175));
var layout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.25f), msgBox.Content.RectTransform), isHorizontal: true);
GUITextBox nameInput = new GUITextBox(new RectTransform(Vector2.One, layout.RectTransform));
msgBox.Buttons[1].OnClicked = delegate
{
msgBox.Close();
return true;
};
msgBox.Buttons[0].OnClicked = delegate
{
OnAccepted.Invoke(nameInput.Text);
msgBox.Close();
return true;
};
}
}
}
@@ -0,0 +1,351 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma
{
internal class NodeConnectionType
{
public static readonly NodeConnectionType Activate = new NodeConnectionType(Side.Left, "Activate");
public static readonly NodeConnectionType Value = new NodeConnectionType(Side.Left, "Value");
public static readonly NodeConnectionType Option = new NodeConnectionType(Side.Right, "Option", new[] { Activate });
public static readonly NodeConnectionType Add = new NodeConnectionType(Side.Right, "Add", new[] { Activate });
public static readonly NodeConnectionType Success = new NodeConnectionType(Side.Right, "Success", new[] { Activate });
public static readonly NodeConnectionType Failure = new NodeConnectionType(Side.Right, "Failure", new[] { Activate });
public static readonly NodeConnectionType Next = new NodeConnectionType(Side.Right, "Next", new[] { Activate });
public static readonly NodeConnectionType Out = new NodeConnectionType(Side.Right, "Out", new[] { Value });
public enum Side
{
Left,
Right
}
public Side NodeSide { get; }
public string Label { get; }
public NodeConnectionType[]? AllowedConnections { get; }
private NodeConnectionType(Side side, string name, NodeConnectionType[]? allowedConnections = null)
{
NodeSide = side;
Label = name;
AllowedConnections = allowedConnections;
}
}
internal class NodeConnection
{
public string Attribute { get; }
public int ID { get; set; }
public bool EndConversation { get; set; }
private string? optionText;
public string? OptionText
{
get => optionText;
set
{
optionText = value;
actualValue = WrappedValue = TextManager.Get(value, true) is { } translated ? translated : value;
}
}
public NodeConnectionType Type { get; }
public Type? ValueType { get; }
private object? overrideValue;
private object? actualValue;
public object? OverrideValue
{
get => overrideValue;
set
{
overrideValue = value;
if (value is string str)
{
actualValue = WrappedValue = TextManager.Get(str, true) is { } translated ? translated : str;
}
else
{
actualValue = WrappedValue = value?.ToString() ?? string.Empty;
}
}
}
private string? wrappedValue;
private string? WrappedValue
{
get => wrappedValue;
set
{
string valueText = value ?? string.Empty;
if (string.IsNullOrWhiteSpace(valueText))
{
wrappedValue = null;
return;
}
Vector2 textSize = GUI.SmallFont.MeasureString(valueText);
bool wasWrapped = false;
while (textSize.X > 96)
{
wasWrapped = true;
valueText = $"{valueText}...".Substring(0, valueText.Length - 4);
textSize = GUI.SmallFont.MeasureString($"{valueText}...");
}
if (wasWrapped)
{
valueText = valueText.TrimEnd(' ') + "...";
}
wrappedValue = valueText;
}
}
public PropertyInfo? PropertyInfo { get; }
public Rectangle DrawRectangle = Rectangle.Empty;
public readonly EditorNode Parent;
public readonly List<NodeConnection> ConnectedTo = new List<NodeConnection>();
private readonly Color bgColor = Color.DarkGray * 0.8f;
private readonly Color outlineColor = Color.White * 0.8f;
public object? GetValue()
{
if (OverrideValue != null)
{
return OverrideValue;
}
foreach (EditorNode editorNode in EventEditorScreen.nodeList)
{
var outNode = editorNode.Connections.Find(connection => connection.Type == NodeConnectionType.Out);
if (outNode != null && outNode.ConnectedTo.Contains(this))
{
return (outNode.Parent as ValueNode)?.Value;
}
}
return null;
}
public void ClearConnections()
{
foreach (var connection in EventEditorScreen.nodeList.SelectMany(editorNode => editorNode.Connections.Where(connection => connection.ConnectedTo.Contains(this))))
{
connection.ConnectedTo.Remove(this);
}
ConnectedTo.Clear();
}
public NodeConnection(EditorNode parent, NodeConnectionType type, string attribute = "", Type? valueType = null, PropertyInfo? propertyInfo = null)
{
Type = type;
ValueType = valueType;
Attribute = attribute;
PropertyInfo = propertyInfo;
Parent = parent;
ID = parent.Connections.Count;
}
private Point GetRenderPos(Rectangle parentRectangle, int yOffset)
{
int x = Type.NodeSide == NodeConnectionType.Side.Left ? parentRectangle.Left - 15 : parentRectangle.Right - 1;
return new Point(x, parentRectangle.Y + 8 + parentRectangle.Height / 8 * yOffset);
}
public void Draw(SpriteBatch spriteBatch, Rectangle parentRectangle, int yOffset)
{
float camZoom = Screen.Selected is EventEditorScreen eventEditor ? eventEditor.Cam.Zoom : 1.0f;
Point pos = GetRenderPos(parentRectangle, yOffset);
DrawRectangle = new Rectangle(pos, new Point(16, 16));
GUI.DrawRectangle(spriteBatch, DrawRectangle, bgColor, isFilled: true);
GUI.DrawRectangle(spriteBatch, DrawRectangle, EndConversation ? GUI.Style.Red : outlineColor, isFilled: false, thickness: (int)Math.Max(1, 1.25f / camZoom));
string label = string.IsNullOrWhiteSpace(Attribute) ? Type.Label : Attribute;
float xPos = parentRectangle.Center.X > pos.X ? 24 : -8 - GUI.SmallFont.MeasureString(label).X;
if (Type != NodeConnectionType.Out)
{
Vector2 size = GUI.SmallFont.MeasureString(label);
Vector2 positon = new Vector2(pos.X + xPos, pos.Y);
Rectangle bgRect = new Rectangle(positon.ToPoint(), size.ToPoint());
bgRect.Inflate(4, 4);
GUI.DrawRectangle(spriteBatch, bgRect, Color.Black * 0.6f, isFilled: true);
GUI.DrawString(spriteBatch, positon, label, GetPropertyColor(ValueType), font: GUI.SmallFont);
Vector2 mousePos = Screen.Selected.Cam.ScreenToWorld(PlayerInput.MousePosition);
mousePos.Y = -mousePos.Y;
if (bgRect.Contains(mousePos))
{
CustomAttributeData? attribute = PropertyInfo?.CustomAttributes.FirstOrDefault();
if (attribute?.AttributeType == typeof(Serialize))
{
if (attribute.ConstructorArguments.Count > 2)
{
string? description = attribute.ConstructorArguments[2].Value as string;
if (!string.IsNullOrWhiteSpace(description))
{
EventEditorScreen.DrawnTooltip = description;
}
}
}
}
}
if (OverrideValue != null)
{
DrawLabel(spriteBatch, new Vector2(DrawRectangle.Center.X - 96, pos.Y + (DrawRectangle.Height / 2) - (20 / 2)), WrappedValue ?? "null", actualValue?.ToString() ?? string.Empty);
}
if (OptionText != null)
{
DrawLabel(spriteBatch, new Vector2(DrawRectangle.Center.X, pos.Y + (DrawRectangle.Height / 2) - (20 / 2)), WrappedValue ?? "null", actualValue?.ToString() ?? string.Empty);
}
if (Parent.IsHighlighted)
{
DrawConnections(spriteBatch, yOffset, Math.Max(8.0f, 8.0f / camZoom), Color.Red);
}
DrawConnections(spriteBatch, yOffset, width: Math.Max(2.0f, 2.0f / camZoom));
if (EventEditorScreen.DraggedConnection == this)
{
DrawSquareLine(spriteBatch, EventEditorScreen.DraggingPosition, yOffset, width: Math.Max(2.0f, 2.0f / camZoom));
}
}
private void DrawConnections(SpriteBatch spriteBatch, int yOffset, float width = 2, Color? overrideColor = null)
{
foreach (NodeConnection? eventNodeConnection in ConnectedTo)
{
if (eventNodeConnection != null)
{
DrawSquareLine(spriteBatch, new Vector2(eventNodeConnection.DrawRectangle.Left + 1, eventNodeConnection.DrawRectangle.Center.Y), yOffset, width, overrideColor);
}
}
}
private void DrawLabel(SpriteBatch spriteBatch, Vector2 pos, string text, string fullText)
{
float camZoom = Screen.Selected is EventEditorScreen eventEditor ? eventEditor.Cam.Zoom : 1.0f;
Rectangle valueRect = new Rectangle((int)pos.X, (int)pos.Y, 96, 20);
Vector2 textSize = GUI.SmallFont.MeasureString(text);
Vector2 position = valueRect.Location.ToVector2() + valueRect.Size.ToVector2() / 2 - textSize / 2;
Rectangle drawRect = valueRect;
drawRect.Inflate(4, 4);
GUI.DrawRectangle(spriteBatch, drawRect, new Color(50, 50, 50), isFilled: true);
GUI.DrawRectangle(spriteBatch, drawRect, EndConversation ? GUI.Style.Red : outlineColor, isFilled: false, thickness: (int)Math.Max(1, 1.25f / camZoom));
GUI.DrawString(spriteBatch, position, text, GetPropertyColor(ValueType), font: GUI.SmallFont);
DrawRectangle = Rectangle.Union(DrawRectangle, drawRect);
if (!string.IsNullOrWhiteSpace(fullText))
{
Vector2 mousePos = Screen.Selected.Cam.ScreenToWorld(PlayerInput.MousePosition);
mousePos.Y = -mousePos.Y;
if (DrawRectangle.Contains(mousePos))
{
EventEditorScreen.DrawnTooltip = fullText;
}
}
}
private void DrawSquareLine(SpriteBatch spriteBatch, Vector2 position, int yOffset, float width = 2, Color? overrideColor = null)
{
// draw a line between 2 nodes using points
// the order of this array is messed up, I know
// order of points is from start node to end node: 0, 4, 1, 2, 5, 3
Vector2[] points = new Vector2[6];
int xOffset = 24 * (yOffset + 1);
points[0] = new Vector2(DrawRectangle.Right, DrawRectangle.Center.Y);
points[3] = position;
points[1] = points[0];
points[2] = points[3];
points[4] = points[1];
points[5] = points[2];
points[1].X += (points[2].X - points[1].X) / 2;
points[1].X = Math.Max(points[1].X, points[0].X + xOffset);
points[2].X = points[1].X;
// if the node is "behind" us do some magic to make the line curve to prevent overlapping
if (points[1].X <= points[0].X + xOffset)
{
points[4].X += xOffset;
points[1].X = points[4].X;
points[1].Y += (points[2].Y - points[1].Y) / 2;
}
if (points[2].X >= points[3].X - xOffset)
{
points[5].X -= xOffset;
points[2].X = points[5].X;
points[2].Y -= points[2].Y - points[1].Y;
}
Color drawColor = Parent is ValueNode ? GetPropertyColor(ValueType) : GUI.Style.Red;
if (overrideColor != null)
{
drawColor = overrideColor.Value;
}
GUI.DrawLine(spriteBatch, points[0], points[4], drawColor, width: (int)width);
GUI.DrawLine(spriteBatch, points[4], points[1], drawColor, width: (int)width);
GUI.DrawLine(spriteBatch, points[1], points[2], drawColor, width: (int)width);
GUI.DrawLine(spriteBatch, points[2], points[5], drawColor, width: (int)width);
GUI.DrawLine(spriteBatch, points[5], points[3], drawColor, width: (int)width);
}
private static readonly Color defaultColor = new Color(139, 233, 253);
private static readonly Color yellowColor = new Color(241, 250, 140);
private static readonly Color pinkColor = new Color(255, 121, 198);
private static readonly Color purpleColor = new Color(189, 147, 249);
public static Color GetPropertyColor(Type? valueType)
{
Color color = defaultColor;
if (valueType == typeof(bool))
color = pinkColor;
else if (valueType == typeof(string))
color = yellowColor;
else if (valueType == typeof(int) ||
valueType == typeof(float) ||
valueType == typeof(double))
color = purpleColor;
else if (valueType == null) color = Color.White;
return color;
}
public bool CanConnect(NodeConnection otherNode)
{
if (otherNode.OverrideValue != null)
{
return false;
}
return Type.AllowedConnections == null || Type.AllowedConnections.Contains(otherNode.Type);
}
}
}