Niagara and Metasound nodes WIP

* Keep track of entities

* Fix renaming

* Niagara variables wip

* Several niagara decode and test

* Move nodeTemplate code to dedicated file, self node added

* Move node decoding functions to dedicated files

* Move pin decoding logic to dedicated files

* Accept space separated keys in objects

* Build

* Prevent a crash in case of incomplete object

* Avoid creating objects unnecessarily

* types formatting

* Initial metasound style

* Common pcg nodes colors

* Fix string serialization

* Metasound new styles and fixes

* More metasound styles and colors

* WIP

* Several fixes

* More tests and fixes

* Clean gitignore
This commit is contained in:
barsdeveloper
2024-05-20 12:56:36 +02:00
committed by GitHub
parent 08e2e8edd8
commit a5813d0b4d
72 changed files with 6903 additions and 4879 deletions

View File

@@ -1,7 +1,8 @@
import Parsernostrum from "parsernostrum"
import Configuration from "../Configuration.js"
import SVGIcon from "../SVGIcon.js"
import Utility from "../Utility.js"
import nodeColor from "../decoding/nodeColor.js"
import nodeIcon from "../decoding/nodeIcon.js"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import FunctionReferenceEntity from "./FunctionReferenceEntity.js"
@@ -14,67 +15,25 @@ import MacroGraphReferenceEntity from "./MacroGraphReferenceEntity.js"
import MirroredEntity from "./MirroredEntity.js"
import ObjectReferenceEntity from "./ObjectReferenceEntity.js"
import PinEntity from "./PinEntity.js"
import ScriptVariableEntity from "./ScriptVariableEntity.js"
import SymbolEntity from "./SymbolEntity.js"
import Union from "./Union.js"
import UnknownPinEntity from "./UnknownPinEntity.js"
import VariableReferenceEntity from "./VariableReferenceEntity.js"
import nodeVariadic from "../decoding/nodeVariadic.js"
export default class ObjectEntity extends IEntity {
static #keyName = {
"A_AccentGrave": "à",
"Add": "Num +",
"C_Cedille": "ç",
"Decimal": "Num .",
"Divide": "Num /",
"E_AccentAigu": "é",
"E_AccentGrave": "è",
"F1": "F1", // Otherwise F and number will be separated
"F10": "F10",
"F11": "F11",
"F12": "F12",
"F2": "F2",
"F3": "F3",
"F4": "F4",
"F5": "F5",
"F6": "F6",
"F7": "F7",
"F8": "F8",
"F9": "F9",
"Gamepad_Special_Left_X": "Touchpad Button X Axis",
"Gamepad_Special_Left_Y": "Touchpad Button Y Axis",
"Mouse2D": "Mouse XY 2D-Axis",
"Multiply": "Num *",
"Section": "§",
"Subtract": "Num -",
"Tilde": "`",
}
static attributes = {
...super.attributes,
isExported: new AttributeInfo({
type: Boolean,
ignored: true,
}),
Class: AttributeInfo.createType(ObjectReferenceEntity),
Name: AttributeInfo.createType(String),
Archetype: AttributeInfo.createType(ObjectReferenceEntity),
ExportPath: AttributeInfo.createType(ObjectReferenceEntity),
R: new AttributeInfo({
type: new Union(Boolean, Number),
default: false,
silent: true,
}),
G: new AttributeInfo({
type: new Union(Boolean, Number),
default: false,
silent: true,
}),
B: new AttributeInfo({
type: new Union(Boolean, Number),
default: false,
silent: true,
}),
A: new AttributeInfo({
type: new Union(Boolean, Number),
default: false,
silent: true,
}),
ObjectRef: AttributeInfo.createType(ObjectReferenceEntity),
BlueprintElementType: AttributeInfo.createType(ObjectReferenceEntity),
BlueprintElementInstance: AttributeInfo.createType(ObjectReferenceEntity),
@@ -101,6 +60,7 @@ export default class ObjectEntity extends IEntity {
ComponentPropertyName: AttributeInfo.createType(String),
EventReference: AttributeInfo.createType(FunctionReferenceEntity),
FunctionReference: AttributeInfo.createType(FunctionReferenceEntity),
FunctionScript: AttributeInfo.createType(ObjectReferenceEntity),
CustomFunctionName: AttributeInfo.createType(String),
TargetType: AttributeInfo.createType(ObjectReferenceEntity),
MacroGraphReference: AttributeInfo.createType(MacroGraphReferenceEntity),
@@ -110,6 +70,16 @@ export default class ObjectEntity extends IEntity {
inlined: true,
}),
InputKey: AttributeInfo.createType(SymbolEntity),
OpName: AttributeInfo.createType(String),
CachedChangeId: AttributeInfo.createType(GuidEntity),
FunctionDisplayName: AttributeInfo.createType(String),
AddedPins: new AttributeInfo({
type: [UnknownPinEntity],
default: () => [],
inlined: true,
silent: true,
}),
ChangeId: AttributeInfo.createType(GuidEntity),
MaterialFunction: AttributeInfo.createType(ObjectReferenceEntity),
bOverrideFunction: AttributeInfo.createType(Boolean),
bInternalEvent: AttributeInfo.createType(Boolean),
@@ -170,11 +140,15 @@ export default class ObjectEntity extends IEntity {
NodeGuid: AttributeInfo.createType(GuidEntity),
ErrorType: AttributeInfo.createType(IntegerEntity),
ErrorMsg: AttributeInfo.createType(String),
ScriptVariables: new AttributeInfo({
type: [ScriptVariableEntity],
inlined: true,
}),
Node: AttributeInfo.createType(new MirroredEntity(ObjectReferenceEntity)),
ExportedNodes: AttributeInfo.createType(String),
CustomProperties: AttributeInfo.createType([new Union(PinEntity, UnknownPinEntity)]),
}
static nameRegex = /^(\w+?)(?:_(\d+))?$/
static sequencerScriptingNameRegex = /\/Script\/SequencerScripting\.MovieSceneScripting(.+)Channel/
static customPropertyGrammar = Parsernostrum.seq(
Parsernostrum.reg(/CustomProperties\s+/),
Grammar.grammarFor(
@@ -226,22 +200,22 @@ export default class ObjectEntity extends IEntity {
static createGrammar() {
return Parsernostrum.seq(
Parsernostrum.reg(/Begin\s+Object/),
Parsernostrum.reg(/Begin +Object/),
Parsernostrum.seq(
Parsernostrum.whitespace,
Parsernostrum.alt(
this.createSubObjectGrammar(),
this.customPropertyGrammar,
Grammar.createAttributeGrammar(this),
Grammar.createAttributeGrammar(this, Parsernostrum.reg(Grammar.Regex.MultipleWordsSymbols)),
Grammar.createAttributeGrammar(this, Grammar.attributeNameQuoted, undefined, (obj, k, v) =>
Utility.objectSet(obj, ["attributes", ...k, "quoted"], true)
),
this.inlinedArrayEntryGrammar,
this.createSubObjectGrammar()
)
)
.map(([_0, entry]) => entry)
.many(),
Parsernostrum.reg(/\s+End\s+Object/),
Parsernostrum.reg(/\s+End +Object/),
)
.map(([_0, attributes, _2]) => {
const values = {}
@@ -250,33 +224,13 @@ export default class ObjectEntity extends IEntity {
})
}
/** @param {String} value */
static keyName(value) {
/** @type {String} */
let result = ObjectEntity.#keyName[value]
if (result) {
return result
}
result = Utility.numberFromText(value)?.toString()
if (result) {
return result
}
const match = value.match(/NumPad([a-zA-Z]+)/)
if (match) {
result = Utility.numberFromText(match[1]).toString()
if (result) {
return "Num " + result
}
}
}
static getMultipleObjectsGrammar() {
return Parsernostrum.seq(
Parsernostrum.whitespaceOpt,
this.createGrammar(),
this.grammar,
Parsernostrum.seq(
Parsernostrum.whitespace,
this.createGrammar(),
this.grammar,
)
.map(([_0, object]) => object)
.many(),
@@ -309,6 +263,7 @@ export default class ObjectEntity extends IEntity {
// Attributes
/** @type {(PinEntity | UnknownPinEntity)[]} */ this.CustomProperties
/** @type {Boolean} */ this.bIsPureFunc
/** @type {Boolean} */ this.isExported
/** @type {FunctionReferenceEntity} */ this.ComponentPropertyName
/** @type {FunctionReferenceEntity} */ this.EventReference
/** @type {FunctionReferenceEntity} */ this.FunctionReference
@@ -339,6 +294,7 @@ export default class ObjectEntity extends IEntity {
/** @type {ObjectReferenceEntity} */ this.Class
/** @type {ObjectReferenceEntity} */ this.Enum
/** @type {ObjectReferenceEntity} */ this.ExportPath
/** @type {ObjectReferenceEntity} */ this.FunctionScript
/** @type {ObjectReferenceEntity} */ this.Graph
/** @type {ObjectReferenceEntity} */ this.MaterialExpression
/** @type {ObjectReferenceEntity} */ this.MaterialExpressionComment
@@ -348,15 +304,19 @@ export default class ObjectEntity extends IEntity {
/** @type {ObjectReferenceEntity} */ this.SettingsInterface
/** @type {ObjectReferenceEntity} */ this.StructType
/** @type {ObjectReferenceEntity} */ this.TargetType
/** @type {ScriptVariableEntity[]} */ this.ScriptVariables
/** @type {String[]} */ this.EnumEntries
/** @type {String[]} */ this.PinNames
/** @type {String} */ this.CustomFunctionName
/** @type {String} */ this.DelegatePropertyName
/** @type {String} */ this.ExportedNodes
/** @type {String} */ this.FunctionDisplayName
/** @type {String} */ this.InputName
/** @type {String} */ this.Name
/** @type {String} */ this.NodeComment
/** @type {String} */ this.NodeTitle
/** @type {String} */ this.Operation
/** @type {String} */ this.OpName
/** @type {String} */ this.ProxyFactoryFunctionName
/** @type {String} */ this.SubgraphInstance
/** @type {String} */ this.Text
@@ -365,6 +325,7 @@ export default class ObjectEntity extends IEntity {
/** @type {SymbolEntity} */ this.InputAxisKey
/** @type {SymbolEntity} */ this.InputKey
/** @type {SymbolEntity} */ this.InputType
/** @type {UnknownPinEntity[]} */ this.AddedPins
/** @type {VariableReferenceEntity} */ this.DelegateReference
/** @type {VariableReferenceEntity} */ this.VariableReference
@@ -402,6 +363,15 @@ export default class ObjectEntity extends IEntity {
obj.G = new MirroredEntity(Boolean, () => rgbaPins[1].DefaultValue)
obj.B = new MirroredEntity(Boolean, () => rgbaPins[2].DefaultValue)
obj.A = new MirroredEntity(Boolean, () => rgbaPins[3].DefaultValue)
Utility.objectSet(obj, ["attributes", "R", "default"], false)
Utility.objectSet(obj, ["attributes", "R", "silent"], true)
Utility.objectSet(obj, ["attributes", "G", "default"], false)
Utility.objectSet(obj, ["attributes", "G", "silent"], true)
Utility.objectSet(obj, ["attributes", "B", "default"], false)
Utility.objectSet(obj, ["attributes", "B", "silent"], true)
Utility.objectSet(obj, ["attributes", "A", "default"], false)
Utility.objectSet(obj, ["attributes", "A", "silent"], true)
obj._keys = [...Configuration.rgba, ...Object.keys(obj).filter(k => !Configuration.rgba.includes(k))]
}
}
/** @type {ObjectEntity} */
@@ -409,24 +379,23 @@ export default class ObjectEntity extends IEntity {
if (pcgObject) {
pcgObject.PositionX && (pcgObject.PositionX.getter = () => this.NodePosX)
pcgObject.PositionY && (pcgObject.PositionY.getter = () => this.NodePosY)
pcgObject.getSubobjects()
.forEach(
/** @param {ObjectEntity} obj */
obj => {
if (obj.Node !== undefined) {
const nodeRef = obj.Node.get()
if (
nodeRef.type === this.PCGNode.type
&& nodeRef.path === `${this.Name}.${this.PCGNode.path}`
) {
obj.Node.getter = () => new ObjectReferenceEntity({
type: this.PCGNode.type,
path: `${this.Name}.${this.PCGNode.path}`,
})
}
pcgObject.getSubobjects().forEach(
/** @param {ObjectEntity} obj */
obj => {
if (obj.Node !== undefined) {
const nodeRef = obj.Node.get()
if (
nodeRef.type === this.PCGNode.type
&& nodeRef.path === `${this.Name}.${this.PCGNode.path}`
) {
obj.Node.getter = () => new ObjectReferenceEntity({
type: this.PCGNode.type,
path: `${this.Name}.${this.PCGNode.path}`,
})
}
}
)
}
)
}
let inputIndex = 0
@@ -477,19 +446,12 @@ export default class ObjectEntity extends IEntity {
/** @returns {[String, Number]} */
getNameAndCounter() {
const result = this.getObjectName(false).match(ObjectEntity.nameRegex)
const result = this.getObjectName().match(ObjectEntity.nameRegex)
let name = ""
let counter = null
if (result) {
if (result.length > 1) {
name = result[1]
}
if (result.length > 2) {
counter = parseInt(result[2])
}
return [name, counter]
}
return ["", 0]
return result
? [result[1] ?? "", parseInt(result[2] ?? "0")]
: ["", 0]
}
getCounter() {
@@ -628,6 +590,10 @@ export default class ObjectEntity extends IEntity {
|| this.getPcgSubobject()
}
isNiagara() {
return this.Class && (this.Class.type ? this.Class.type : this.Class.path)?.startsWith("/Script/NiagaraEditor.")
}
/** @return {ObjectEntity} */
getPcgSubobject() {
const node = this.PCGNode
@@ -646,9 +612,9 @@ export default class ObjectEntity extends IEntity {
/** @return {ObjectEntity} */
getSubgraphObject() {
const node = this.SubgraphInstance
return node
? this[Configuration.subObjectAttributeNameFromName(node)]
const name = this.SubgraphInstance
return name
? this[Configuration.subObjectAttributeNameFromName(name)]
: null
}
@@ -666,594 +632,15 @@ export default class ObjectEntity extends IEntity {
return this.getCustomproperties().find(pin => pin.PinType.PinCategory === "delegate")
}
/** @returns {String} */
nodeDisplayName() {
let input
switch (this.getType()) {
case Configuration.paths.asyncAction:
if (this.ProxyFactoryFunctionName) {
return Utility.formatStringName(this.ProxyFactoryFunctionName)
}
case Configuration.paths.actorBoundEvent:
case Configuration.paths.componentBoundEvent:
return `${Utility.formatStringName(this.DelegatePropertyName)} (${this.ComponentPropertyName ?? "Unknown"})`
case Configuration.paths.callDelegate:
return `Call ${this.DelegateReference?.MemberName ?? "None"}`
case Configuration.paths.createDelegate:
return "Create Event"
case Configuration.paths.customEvent:
if (this.CustomFunctionName) {
return this.CustomFunctionName
}
case Configuration.paths.dynamicCast:
if (!this.TargetType) {
return "Bad cast node" // Target type not found
}
return `Cast To ${this.TargetType?.getName()}`
case Configuration.paths.enumLiteral:
return `Literal enum ${this.Enum?.getName()}`
case Configuration.paths.event:
return `Event ${(this.EventReference?.MemberName ?? "").replace(/^Receive/, "")}`
case Configuration.paths.executionSequence:
return "Sequence"
case Configuration.paths.forEachElementInEnum:
return `For Each ${this.Enum?.getName()}`
case Configuration.paths.forEachLoopWithBreak:
return "For Each Loop with Break"
case Configuration.paths.functionEntry:
return this.FunctionReference?.MemberName === "UserConstructionScript"
? "Construction Script"
: this.FunctionReference?.MemberName
case Configuration.paths.functionResult:
return "Return Node"
case Configuration.paths.ifThenElse:
return "Branch"
case Configuration.paths.makeStruct:
if (this.StructType) {
return `Make ${this.StructType.getName()}`
}
case Configuration.paths.materialExpressionComponentMask: {
const materialObject = this.getMaterialSubobject()
return `Mask ( ${Configuration.rgba
.filter(k => /** @type {MirroredEntity} */(materialObject[k]).get() === true)
.map(v => v + " ")
.join("")})`
}
case Configuration.paths.materialExpressionConstant:
input ??= [this.getCustomproperties().find(pinEntity => pinEntity.PinName == "Value")?.DefaultValue]
case Configuration.paths.materialExpressionConstant2Vector:
input ??= [
this.getCustomproperties().find(pinEntity => pinEntity.PinName == "X")?.DefaultValue,
this.getCustomproperties().find(pinEntity => pinEntity.PinName == "Y")?.DefaultValue,
]
case Configuration.paths.materialExpressionConstant3Vector:
if (!input) {
/** @type {VectorEntity} */
const vector = this.getCustomproperties()
.find(pinEntity => pinEntity.PinName == "Constant")
?.DefaultValue
input = [vector.X, vector.Y, vector.Z]
}
case Configuration.paths.materialExpressionConstant4Vector:
if (!input) {
/** @type {LinearColorEntity} */
const vector = this.getCustomproperties()
.find(pinEntity => pinEntity.PinName == "Constant")
?.DefaultValue
input = [vector.R, vector.G, vector.B, vector.A].map(v => v.valueOf())
}
if (input.length > 0) {
return input.map(v => Utility.printExponential(v)).reduce((acc, cur) => acc + "," + cur)
}
break
case Configuration.paths.materialExpressionFunctionInput: {
const materialObject = this.getMaterialSubobject()
const inputName = materialObject?.InputName ?? "In"
const inputType = materialObject?.InputType?.value.match(/^.+?_(\w+)$/)?.[1] ?? "Vector3"
return `Input ${inputName} (${inputType})`
}
case Configuration.paths.materialExpressionLogarithm:
return "Ln"
case Configuration.paths.materialExpressionLogarithm10:
return "Log10"
case Configuration.paths.materialExpressionLogarithm2:
return "Log2"
case Configuration.paths.materialExpressionMaterialFunctionCall:
const materialFunction = this.getMaterialSubobject()?.MaterialFunction
if (materialFunction) {
return materialFunction.getName()
}
break
case Configuration.paths.materialExpressionSquareRoot:
return "Sqrt"
case Configuration.paths.pcgEditorGraphNodeInput:
return "Input"
case Configuration.paths.pcgEditorGraphNodeOutput:
return "Output"
case Configuration.paths.spawnActorFromClass:
return `SpawnActor ${Utility.formatStringName(
this.getCustomproperties().find(pinEntity => pinEntity.getType() == "class")?.DefaultObject?.getName()
?? "NONE"
)}`
case Configuration.paths.switchEnum:
return `Switch on ${this.Enum?.getName() ?? "Enum"}`
case Configuration.paths.switchInteger:
return `Switch on Int`
case Configuration.paths.variableGet:
return ""
case Configuration.paths.variableSet:
return "SET"
}
let switchTarget = this.switchTarget()
if (switchTarget) {
if (switchTarget[0] !== "E") {
switchTarget = Utility.formatStringName(switchTarget)
}
return `Switch on ${switchTarget}`
}
if (this.isComment()) {
return this.NodeComment
}
const keyNameSymbol = this.getHIDAttribute()
if (keyNameSymbol) {
const keyName = keyNameSymbol.toString()
let title = ObjectEntity.keyName(keyName) ?? Utility.formatStringName(keyName)
if (this.getClass() === Configuration.paths.inputDebugKey) {
title = "Debug Key " + title
} else if (this.getClass() === Configuration.paths.getInputAxisKeyValue) {
title = "Get " + title
}
return title
}
if (this.getClass() === Configuration.paths.macro) {
return Utility.formatStringName(this.MacroGraphReference?.getMacroName())
}
if (this.isMaterial() && this.getMaterialSubobject()) {
let result = this.getMaterialSubobject().nodeDisplayName()
result = result.match(/Material Expression (.+)/)?.[1] ?? result
return result
}
if (this.isPcg() && this.getPcgSubobject()) {
let pcgSubobject = this.getPcgSubobject()
let result = pcgSubobject.NodeTitle ? pcgSubobject.NodeTitle : pcgSubobject.nodeDisplayName()
return result
}
const subgraphObject = this.getSubgraphObject()
if (subgraphObject) {
return subgraphObject.Graph.getName()
}
const settingsObject = this.getSettingsObject()
if (settingsObject) {
if (settingsObject.ExportPath.type === Configuration.paths.pcgHiGenGridSizeSettings) {
return `Grid Size: ${(
settingsObject.HiGenGridSize?.toString().match(/\d+/)?.[0]?.concat("00")
?? settingsObject.HiGenGridSize?.toString().match(/^\w+$/)?.[0]
) ?? "256"}`
}
if (settingsObject.BlueprintElementInstance) {
return Utility.formatStringName(settingsObject.BlueprintElementType.getName())
}
if (settingsObject.Operation) {
const match = settingsObject.Name.match(/PCGMetadata(\w+)Settings_\d+/)
if (match) {
return Utility.formatStringName(match[1] + ": " + settingsObject.Operation)
}
}
const settingsSubgraphObject = settingsObject.getSubgraphObject()
if (settingsSubgraphObject && settingsSubgraphObject.Graph) {
return settingsSubgraphObject.Graph.getName()
}
}
let memberName = this.FunctionReference?.MemberName
if (memberName) {
const memberParent = this.FunctionReference.MemberParent?.path ?? ""
switch (memberName) {
case "AddKey":
let result = memberParent.match(ObjectEntity.sequencerScriptingNameRegex)
if (result) {
return `Add Key (${Utility.formatStringName(result[1])})`
}
case "Concat_StrStr":
return "Append"
}
const memberNameTraceLineMatch = memberName.match(Configuration.lineTracePattern)
if (memberNameTraceLineMatch) {
return "Line Trace"
+ (memberNameTraceLineMatch[1] === "Multi" ? " Multi " : " ")
+ (memberNameTraceLineMatch[2] === ""
? "By Channel"
: Utility.formatStringName(memberNameTraceLineMatch[2])
)
}
switch (memberParent) {
case Configuration.paths.blueprintGameplayTagLibrary:
case Configuration.paths.kismetMathLibrary:
case Configuration.paths.slateBlueprintLibrary:
case Configuration.paths.timeManagementBlueprintLibrary:
const leadingLetter = memberName.match(/[BF]([A-Z]\w+)/)
if (leadingLetter) {
// Some functions start with B or F (Like FCeil, FMax, BMin)
memberName = leadingLetter[1]
}
switch (memberName) {
case "Abs": return "ABS"
case "BooleanAND": return "AND"
case "BooleanNAND": return "NAND"
case "BooleanOR": return "OR"
case "Exp": return "e"
case "LineTraceSingle": return "Line Trace By Channel"
case "Max": return "MAX"
case "MaxInt64": return "MAX"
case "Min": return "MIN"
case "MinInt64": return "MIN"
case "Not_PreBool": return "NOT"
case "Sin": return "SIN"
case "Sqrt": return "SQRT"
case "Square": return "^2"
// Dot products not respecting MemberName pattern
case "CrossProduct2D": return "cross"
case "Vector4_CrossProduct3": return "cross3"
case "DotProduct2D":
case "Vector4_DotProduct":
return "dot"
case "Vector4_DotProduct3": return "dot3"
}
if (memberName.startsWith("Add_")) {
return "+"
}
if (memberName.startsWith("And_")) {
return "&"
}
if (memberName.startsWith("Conv_")) {
return "" // Conversion nodes do not have visible names
}
if (memberName.startsWith("Cross_")) {
return "cross"
}
if (memberName.startsWith("Divide_")) {
return String.fromCharCode(0x00f7)
}
if (memberName.startsWith("Dot_")) {
return "dot"
}
if (memberName.startsWith("EqualEqual_")) {
return "=="
}
if (memberName.startsWith("Greater_")) {
return ">"
}
if (memberName.startsWith("GreaterEqual_")) {
return ">="
}
if (memberName.startsWith("Less_")) {
return "<"
}
if (memberName.startsWith("LessEqual_")) {
return "<="
}
if (memberName.startsWith("Multiply_")) {
return String.fromCharCode(0x2a2f)
}
if (memberName.startsWith("Not_")) {
return "~"
}
if (memberName.startsWith("NotEqual_")) {
return "!="
}
if (memberName.startsWith("Or_")) {
return "|"
}
if (memberName.startsWith("Percent_")) {
return "%"
}
if (memberName.startsWith("Subtract_")) {
return "-"
}
if (memberName.startsWith("Xor_")) {
return "^"
}
break
case Configuration.paths.blueprintSetLibrary:
{
const setOperationMatch = memberName.match(/Set_(\w+)/)
if (setOperationMatch) {
return Utility.formatStringName(setOperationMatch[1]).toUpperCase()
}
}
break
case Configuration.paths.blueprintMapLibrary:
{
const setOperationMatch = memberName.match(/Map_(\w+)/)
if (setOperationMatch) {
return Utility.formatStringName(setOperationMatch[1]).toUpperCase()
}
}
break
case Configuration.paths.kismetArrayLibrary:
{
const arrayOperationMath = memberName.match(/Array_(\w+)/)
if (arrayOperationMath) {
return arrayOperationMath[1].toUpperCase()
}
}
break
}
return Utility.formatStringName(memberName)
}
if (this.ObjectRef) {
return this.ObjectRef.getName()
}
return Utility.formatStringName(this.getNameAndCounter()[0])
}
nodeColor() {
switch (this.getType()) {
case Configuration.paths.materialExpressionConstant2Vector:
case Configuration.paths.materialExpressionConstant3Vector:
case Configuration.paths.materialExpressionConstant4Vector:
return Configuration.nodeColors.yellow
case Configuration.paths.makeStruct:
return Configuration.nodeColors.darkBlue
case Configuration.paths.materialExpressionMaterialFunctionCall:
return Configuration.nodeColors.blue
case Configuration.paths.materialExpressionFunctionInput:
return Configuration.nodeColors.red
case Configuration.paths.materialExpressionTextureSample:
return Configuration.nodeColors.darkTurquoise
case Configuration.paths.materialExpressionTextureCoordinate:
return Configuration.nodeColors.red
case Configuration.paths.pcgEditorGraphNodeInput:
case Configuration.paths.pcgEditorGraphNodeOutput:
return Configuration.nodeColors.red
}
switch (this.getClass()) {
case Configuration.paths.callFunction:
return this.bIsPureFunc
? Configuration.nodeColors.green
: Configuration.nodeColors.blue
case Configuration.paths.dynamicCast:
return Configuration.nodeColors.turquoise
case Configuration.paths.inputDebugKey:
case Configuration.paths.inputKey:
return Configuration.nodeColors.red
case Configuration.paths.createDelegate:
case Configuration.paths.enumLiteral:
case Configuration.paths.makeArray:
case Configuration.paths.makeMap:
case Configuration.paths.materialGraphNode:
case Configuration.paths.select:
return Configuration.nodeColors.green
case Configuration.paths.executionSequence:
case Configuration.paths.ifThenElse:
case Configuration.paths.macro:
case Configuration.paths.multiGate:
return Configuration.nodeColors.gray
case Configuration.paths.functionEntry:
case Configuration.paths.functionResult:
return Configuration.nodeColors.violet
case Configuration.paths.timeline:
return Configuration.nodeColors.yellow
}
if (this.switchTarget()) {
return Configuration.nodeColors.lime
}
if (this.isEvent()) {
return Configuration.nodeColors.red
}
if (this.isComment()) {
return (this.CommentColor ? this.CommentColor : LinearColorEntity.getWhite())
.toDimmedColor()
.toCSSRGBValues()
}
const pcgSubobject = this.getPcgSubobject()
if (pcgSubobject && pcgSubobject.NodeTitleColor) {
return pcgSubobject.NodeTitleColor.toDimmedColor(0.1).toCSSRGBValues()
}
if (this.bIsPureFunc) {
return Configuration.nodeColors.green
}
return Configuration.nodeColors.blue
return nodeColor(this)
}
nodeIcon() {
if (this.isMaterial() || this.isPcg()) {
return null
}
switch (this.getType()) {
case Configuration.paths.addDelegate:
case Configuration.paths.asyncAction:
case Configuration.paths.callDelegate:
case Configuration.paths.createDelegate:
case Configuration.paths.functionEntry:
case Configuration.paths.functionResult:
return SVGIcon.node
case Configuration.paths.customEvent: return SVGIcon.event
case Configuration.paths.doN: return SVGIcon.doN
case Configuration.paths.doOnce: return SVGIcon.doOnce
case Configuration.paths.dynamicCast: return SVGIcon.cast
case Configuration.paths.enumLiteral: return SVGIcon.enum
case Configuration.paths.event: return SVGIcon.event
case Configuration.paths.executionSequence:
case Configuration.paths.multiGate:
return SVGIcon.sequence
case Configuration.paths.flipflop:
return SVGIcon.flipflop
case Configuration.paths.forEachElementInEnum:
case Configuration.paths.forLoop:
case Configuration.paths.forLoopWithBreak:
case Configuration.paths.whileLoop:
return SVGIcon.loop
case Configuration.paths.forEachLoop:
case Configuration.paths.forEachLoopWithBreak:
return SVGIcon.forEachLoop
case Configuration.paths.ifThenElse: return SVGIcon.branchNode
case Configuration.paths.isValid: return SVGIcon.questionMark
case Configuration.paths.makeArray: return SVGIcon.makeArray
case Configuration.paths.makeMap: return SVGIcon.makeMap
case Configuration.paths.makeSet: return SVGIcon.makeSet
case Configuration.paths.makeStruct: return SVGIcon.makeStruct
case Configuration.paths.select: return SVGIcon.select
case Configuration.paths.spawnActorFromClass: return SVGIcon.spawnActor
case Configuration.paths.timeline: return SVGIcon.timer
}
if (this.switchTarget()) {
return SVGIcon.switch
}
if (this.nodeDisplayName().startsWith("Break")) {
return SVGIcon.breakStruct
}
if (this.getClass() === Configuration.paths.macro) {
return SVGIcon.macro
}
const hidValue = this.getHIDAttribute()?.toString()
if (hidValue) {
if (hidValue.includes("Mouse")) {
return SVGIcon.mouse
} else if (hidValue.includes("Gamepad_Special")) {
return SVGIcon.keyboard // This is called Touchpad in UE
} else if (hidValue.includes("Gamepad") || hidValue.includes("Steam")) {
return SVGIcon.gamepad
} else if (hidValue.includes("Touch")) {
return SVGIcon.touchpad
} else {
return SVGIcon.keyboard
}
}
if (this.getDelegatePin()) {
return SVGIcon.event
}
if (this.ObjectRef?.type === Configuration.paths.ambientSound) {
return SVGIcon.sound
}
return SVGIcon.functionSymbol
return nodeIcon(this)
}
additionalPinInserter() {
/** @type {() => PinEntity[]} */
let pinEntities
/** @type {(pinEntity: PinEntity) => Number} */
let pinIndexFromEntity
/** @type {(newPinIndex: Number, minIndex: Number, maxIndex: Number) => String} */
let pinNameFromIndex
switch (this.getType()) {
case Configuration.paths.commutativeAssociativeBinaryOperator:
case Configuration.paths.promotableOperator:
switch (this.FunctionReference?.MemberName) {
default:
if (
!this.FunctionReference?.MemberName?.startsWith("Add_")
&& !this.FunctionReference?.MemberName?.startsWith("Subtract_")
&& !this.FunctionReference?.MemberName?.startsWith("Multiply_")
&& !this.FunctionReference?.MemberName?.startsWith("Divide_")
) {
break
}
case "And_Int64Int64":
case "And_IntInt":
case "BMax":
case "BMin":
case "BooleanAND":
case "BooleanNAND":
case "BooleanOR":
case "Concat_StrStr":
case "FMax":
case "FMin":
case "Max":
case "MaxInt64":
case "Min":
case "MinInt64":
case "Or_Int64Int64":
case "Or_IntInt":
pinEntities ??= () => this.getPinEntities().filter(pinEntity => pinEntity.isInput())
pinIndexFromEntity ??= pinEntity =>
pinEntity.PinName.match(/^\s*([A-Z])\s*$/)?.[1]?.charCodeAt(0) - "A".charCodeAt(0)
pinNameFromIndex ??= (index, min = -1, max = -1) => {
const result = String.fromCharCode(index >= 0 ? index : max + "A".charCodeAt(0) + 1)
this.NumAdditionalInputs = pinEntities().length - 1
return result
}
break
}
break
case Configuration.paths.multiGate:
pinEntities ??= () => this.getPinEntities().filter(pinEntity => pinEntity.isOutput())
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*Out[_\s]+(\d+)\s*$/i)?.[1])
pinNameFromIndex ??= (index, min = -1, max = -1) =>
`Out ${index >= 0 ? index : min > 0 ? "Out 0" : max + 1}`
break
case Configuration.paths.switchInteger:
pinEntities ??= () => this.getPinEntities().filter(pinEntity => pinEntity.isOutput())
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*(\d+)\s*$/)?.[1])
pinNameFromIndex ??= (index, min = -1, max = -1) => (index < 0 ? max + 1 : index).toString()
break
case Configuration.paths.switchGameplayTag:
pinNameFromIndex ??= (index, min = -1, max = -1) => {
const result = `Case_${index >= 0 ? index : min > 0 ? "0" : max + 1}`
this.PinNames ??= []
this.PinNames.push(result)
delete this.PinTags[this.PinTags.length - 1]
this.PinTags[this.PinTags.length] = null
return result
}
case Configuration.paths.switchName:
case Configuration.paths.switchString:
pinEntities ??= () => this.getPinEntities().filter(pinEntity => pinEntity.isOutput())
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*Case[_\s]+(\d+)\s*$/i)?.[1])
pinNameFromIndex ??= (index, min = -1, max = -1) => {
const result = `Case_${index >= 0 ? index : min > 0 ? "0" : max + 1}`
this.PinNames ??= []
this.PinNames.push(result)
return result
}
break
}
if (pinEntities) {
return () => {
let min = Number.MAX_SAFE_INTEGER
let max = Number.MIN_SAFE_INTEGER
let values = []
const modelPin = pinEntities().reduce(
(acc, cur) => {
const value = pinIndexFromEntity(cur)
if (!isNaN(value)) {
values.push(value)
min = Math.min(value, min)
if (value > max) {
max = value
return cur
}
} else if (acc === undefined) {
return cur
}
return acc
},
undefined
)
if (min === Number.MAX_SAFE_INTEGER || max === Number.MIN_SAFE_INTEGER) {
min = undefined
max = undefined
}
if (!modelPin) {
return null
}
values.sort((a, b) => a < b ? -1 : a === b ? 0 : 1)
let prev = values[0]
let index = values.findIndex(
// Search for a gap
value => {
const result = value - prev > 1
prev = value
return result
}
)
const newPin = new PinEntity(modelPin)
newPin.PinId = GuidEntity.generateGuid()
newPin.PinName = pinNameFromIndex(index, min, max)
newPin.PinToolTip = undefined
this.getCustomproperties(true).push(newPin)
return newPin
}
}
return nodeVariadic(this)
}
}