3 using System.Collections.Generic;
5 using System.Reflection;
6 using Microsoft.Xna.Framework;
7 using Microsoft.Xna.Framework.Graphics;
11 internal class NodeConnectionType
13 public static readonly NodeConnectionType Activate =
new NodeConnectionType(Side.Left,
"Activate");
14 public static readonly NodeConnectionType Value =
new NodeConnectionType(Side.Left,
"Value");
15 public static readonly NodeConnectionType Option =
new NodeConnectionType(Side.Right,
"Option",
new[] { Activate });
16 public static readonly NodeConnectionType Add =
new NodeConnectionType(Side.Right,
"Add",
new[] { Activate });
17 public static readonly NodeConnectionType Success =
new NodeConnectionType(Side.Right,
"Success",
new[] { Activate });
18 public static readonly NodeConnectionType Failure =
new NodeConnectionType(Side.Right,
"Failure",
new[] { Activate });
19 public static readonly NodeConnectionType Next =
new NodeConnectionType(Side.Right,
"Next",
new[] { Activate });
20 public static readonly NodeConnectionType Out =
new NodeConnectionType(Side.Right,
"Out",
new[] { Value });
28 public Side NodeSide {
get; }
30 public string Label {
get; }
32 public NodeConnectionType[]? AllowedConnections {
get; }
34 private NodeConnectionType(Side side,
string name, NodeConnectionType[]? allowedConnections =
null)
38 AllowedConnections = allowedConnections;
42 internal class EventEditorNodeConnection
44 public string Attribute {
get; }
46 public int ID {
get;
set; }
48 public bool EndConversation {
get;
set; }
50 private string? optionText;
52 public string? OptionText
60 actualValue = WrappedValue = TextManager.Get(value).Fallback(value).Value;
64 actualValue = WrappedValue = value;
69 public NodeConnectionType Type {
get; }
71 public Type? ValueType {
get; }
73 private object? overrideValue;
74 private object? actualValue;
76 public object? OverrideValue
81 overrideValue = value;
82 if (value is
string str)
84 actualValue = WrappedValue = TextManager.Get(str).Fallback(str).Value;
88 actualValue = WrappedValue = value?.ToString() ??
string.Empty;
93 private string? wrappedValue;
95 private string? WrappedValue
100 string valueText = value ??
string.Empty;
101 if (
string.IsNullOrWhiteSpace(valueText))
106 Vector2 textSize = GUIStyle.SmallFont.MeasureString(valueText);
107 bool wasWrapped =
false;
108 while (textSize.X > 96)
111 valueText = $
"{valueText}...".Substring(0, valueText.Length - 4);
112 textSize = GUIStyle.SmallFont.MeasureString($
"{valueText}...");
117 valueText = valueText.TrimEnd(
' ') +
"...";
121 wrappedValue = valueText;
125 public PropertyInfo? PropertyInfo {
get; }
129 public readonly EditorNode Parent;
131 public readonly List<EventEditorNodeConnection> ConnectedTo =
new List<EventEditorNodeConnection>();
133 private readonly Color bgColor = Color.DarkGray * 0.8f;
135 private readonly Color outlineColor = Color.White * 0.8f;
137 public object? GetValue()
139 if (OverrideValue !=
null)
141 return OverrideValue;
144 foreach (EditorNode editorNode
in EventEditorScreen.nodeList)
146 var outNode = editorNode.Connections.Find(connection => connection.Type == NodeConnectionType.Out);
147 if (outNode !=
null && outNode.ConnectedTo.Contains(
this))
149 return (outNode.Parent as ValueNode)?.Value;
156 public void ClearConnections()
158 foreach (var connection
in EventEditorScreen.nodeList.SelectMany(editorNode => editorNode.Connections.Where(connection => connection.ConnectedTo.Contains(
this))))
160 connection.ConnectedTo.Remove(
this);
166 public EventEditorNodeConnection(EditorNode parent, NodeConnectionType type,
string attribute =
"", Type? valueType =
null, PropertyInfo? propertyInfo =
null)
169 ValueType = valueType;
170 Attribute = attribute;
171 PropertyInfo = propertyInfo;
173 ID = parent.Connections.Count;
176 private Point GetRenderPos(Rectangle parentRectangle,
int yOffset)
178 int x = Type.NodeSide == NodeConnectionType.Side.Left ? parentRectangle.Left - 15 : parentRectangle.Right - 1;
179 return new Point(x, parentRectangle.Y + 8 + parentRectangle.Height / 8 * yOffset);
182 public void Draw(SpriteBatch spriteBatch, Rectangle parentRectangle,
int yOffset)
184 float camZoom = Screen.Selected is EventEditorScreen eventEditor ? eventEditor.Cam.Zoom : 1.0f;
185 Point pos = GetRenderPos(parentRectangle, yOffset);
186 DrawRectangle =
new Rectangle(pos,
new Point(16, 16));
187 GUI.DrawRectangle(spriteBatch, DrawRectangle, bgColor, isFilled:
true);
188 GUI.DrawRectangle(spriteBatch, DrawRectangle, EndConversation ? GUIStyle.Red : outlineColor, isFilled:
false, thickness: (
int)Math.Max(1, 1.25f / camZoom));
190 string label =
string.IsNullOrWhiteSpace(Attribute) ? Type.Label : Attribute;
191 float xPos = parentRectangle.Center.X > pos.X ? 24 : -8 - GUIStyle.SmallFont.MeasureString(label).X;
193 if (Type != NodeConnectionType.Out)
195 Vector2 size = GUIStyle.SmallFont.MeasureString(label);
196 Vector2 positon =
new Vector2(pos.X + xPos, pos.Y);
198 bgRect.Inflate(4, 4);
200 GUI.DrawRectangle(spriteBatch, bgRect, Color.Black * 0.6f, isFilled:
true);
201 GUI.DrawString(spriteBatch, positon, label, GetPropertyColor(ValueType), font: GUIStyle.SmallFont);
203 Vector2 mousePos = Screen.Selected.Cam.ScreenToWorld(PlayerInput.MousePosition);
204 mousePos.Y = -mousePos.Y;
205 if (bgRect.Contains(mousePos))
207 CustomAttributeData? attribute = PropertyInfo?.CustomAttributes.FirstOrDefault();
208 if (attribute?.AttributeType == typeof(Serialize))
210 if (attribute.ConstructorArguments.Count > 2)
212 string? description = attribute.ConstructorArguments[2].Value as string;
213 if (!
string.IsNullOrWhiteSpace(description))
215 EventEditorScreen.DrawnTooltip = description;
222 if (OverrideValue !=
null)
224 DrawLabel(spriteBatch,
new Vector2(DrawRectangle.Center.X - 96, pos.Y + (DrawRectangle.Height / 2) - (20 / 2)), WrappedValue ??
"null", actualValue?.ToString() ??
string.Empty);
227 if (OptionText !=
null)
229 DrawLabel(spriteBatch,
new Vector2(DrawRectangle.Center.X, pos.Y + (DrawRectangle.Height / 2) - (20 / 2)), WrappedValue ??
"null", actualValue?.ToString() ??
string.Empty);
232 if (Parent.IsHighlighted)
234 DrawConnections(spriteBatch, yOffset, Math.Max(8.0f, 8.0f / camZoom), Color.Red);
237 DrawConnections(spriteBatch, yOffset, width: Math.Max(2.0f, 2.0f / camZoom));
239 if (EventEditorScreen.DraggedConnection ==
this)
241 DrawSquareLine(spriteBatch, EventEditorScreen.DraggingPosition, yOffset, width: Math.Max(2.0f, 2.0f / camZoom));
245 private void DrawConnections(SpriteBatch spriteBatch,
int yOffset,
float width = 2, Color? overrideColor =
null)
247 foreach (EventEditorNodeConnection? eventNodeConnection
in ConnectedTo)
249 if (eventNodeConnection !=
null)
251 DrawSquareLine(spriteBatch,
new Vector2(eventNodeConnection.DrawRectangle.Left + 1, eventNodeConnection.DrawRectangle.Center.Y), yOffset, width, overrideColor);
256 private void DrawLabel(SpriteBatch spriteBatch, Vector2 pos,
string text,
string fullText)
258 float camZoom = Screen.Selected is EventEditorScreen eventEditor ? eventEditor.Cam.Zoom : 1.0f;
260 Vector2 textSize = GUIStyle.SmallFont.MeasureString(text);
261 Vector2 position = valueRect.Location.ToVector2() + valueRect.Size.ToVector2() / 2 - textSize / 2;
263 drawRect.Inflate(4, 4);
264 GUI.DrawRectangle(spriteBatch, drawRect,
new Color(50, 50, 50), isFilled:
true);
265 GUI.DrawRectangle(spriteBatch, drawRect, EndConversation ? GUIStyle.Red : outlineColor, isFilled:
false, thickness: (
int)Math.Max(1, 1.25f / camZoom));
266 GUI.DrawString(spriteBatch, position, text, GetPropertyColor(ValueType), font: GUIStyle.SmallFont);
267 DrawRectangle =
Rectangle.Union(DrawRectangle, drawRect);
269 if (!
string.IsNullOrWhiteSpace(fullText))
271 Vector2 mousePos = Screen.Selected.Cam.ScreenToWorld(PlayerInput.MousePosition);
272 mousePos.Y = -mousePos.Y;
273 if (DrawRectangle.Contains(mousePos))
275 EventEditorScreen.DrawnTooltip = fullText;
280 private void DrawSquareLine(SpriteBatch spriteBatch, Vector2 position,
int yOffset,
float width = 2, Color? overrideColor =
null)
282 float knobLength = 24 * (yOffset + 1);
283 Vector2 start =
new Vector2(DrawRectangle.Right, DrawRectangle.Center.Y);
284 var (points, _) = ToolBox.GetSquareLineBetweenPoints(start, position, knobLength);
286 Color drawColor = Parent is ValueNode ? GetPropertyColor(ValueType) : GUIStyle.Red;
288 if (overrideColor !=
null)
290 drawColor = overrideColor.Value;
293 GUI.DrawLine(spriteBatch, points[0], points[1], drawColor, width: (
int)width);
294 GUI.DrawLine(spriteBatch, points[1], points[2], drawColor, width: (
int)width);
295 GUI.DrawLine(spriteBatch, points[2], points[3], drawColor, width: (
int)width);
296 GUI.DrawLine(spriteBatch, points[3], points[4], drawColor, width: (
int)width);
297 GUI.DrawLine(spriteBatch, points[4], points[5], drawColor, width: (
int)width);
300 private static readonly Color defaultColor =
new Color(139, 233, 253);
301 private static readonly Color yellowColor =
new Color(241, 250, 140);
302 private static readonly Color pinkColor =
new Color(255, 121, 198);
303 private static readonly Color purpleColor =
new Color(189, 147, 249);
305 public static Color GetPropertyColor(Type? valueType)
307 Color color = defaultColor;
308 if (valueType == typeof(
bool))
310 else if (valueType == typeof(
string))
312 else if (valueType == typeof(
int) ||
313 valueType == typeof(
float) ||
314 valueType == typeof(
double))
316 else if (valueType ==
null) color = Color.White;
320 public bool CanConnect(EventEditorNodeConnection otherNode)
322 if (otherNode.OverrideValue !=
null)
327 return Type.AllowedConnections ==
null || Type.AllowedConnections.Contains(otherNode.Type);