mirror of
https://github.com/barsdeveloper/ueblueprint.git
synced 2026-02-03 23:55:04 +08:00
* WIP * Fix type 1 * Missing types info * Some fixes * Several types refactoring and fixes * WIP * Fix grammar
1332 lines
52 KiB
JavaScript
Executable File
1332 lines
52 KiB
JavaScript
Executable File
import Configuration from "../Configuration.js"
|
|
import FunctionReferenceEntity from "./FunctionReferenceEntity.js"
|
|
import Grammar from "../serialization/Grammar.js"
|
|
import GuidEntity from "./GuidEntity.js"
|
|
import IdentifierEntity from "./IdentifierEntity.js"
|
|
import IEntity from "./IEntity.js"
|
|
import IntegerEntity from "./IntegerEntity.js"
|
|
import LinearColorEntity from "./LinearColorEntity.js"
|
|
import MacroGraphReferenceEntity from "./MacroGraphReferenceEntity.js"
|
|
import MirroredEntity from "./MirroredEntity.js"
|
|
import ObjectReferenceEntity from "./ObjectReferenceEntity.js"
|
|
import Parsimmon from "parsimmon"
|
|
import PinEntity from "./PinEntity.js"
|
|
import SVGIcon from "../SVGIcon.js"
|
|
import SymbolEntity from "./SymbolEntity.js"
|
|
import Union from "./Union.js"
|
|
import UnknownPinEntity from "./UnknownPinEntity.js"
|
|
import Utility from "../Utility.js"
|
|
import VariableReferenceEntity from "./VariableReferenceEntity.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,
|
|
Class: {
|
|
type: ObjectReferenceEntity,
|
|
},
|
|
Name: {
|
|
type: String,
|
|
},
|
|
Archetype: {
|
|
type: ObjectReferenceEntity,
|
|
},
|
|
ExportPath: {
|
|
type: ObjectReferenceEntity,
|
|
},
|
|
ObjectRef: {
|
|
type: ObjectReferenceEntity,
|
|
},
|
|
BlueprintElementType: {
|
|
type: ObjectReferenceEntity
|
|
},
|
|
BlueprintElementInstance: {
|
|
type: ObjectReferenceEntity
|
|
},
|
|
PinTags: {
|
|
type: [null],
|
|
inlined: true,
|
|
},
|
|
PinNames: {
|
|
type: [String],
|
|
inlined: true,
|
|
},
|
|
AxisKey: {
|
|
type: SymbolEntity,
|
|
},
|
|
InputAxisKey: {
|
|
type: SymbolEntity,
|
|
},
|
|
NumAdditionalInputs: {
|
|
type: Number,
|
|
},
|
|
bIsPureFunc: {
|
|
type: Boolean,
|
|
},
|
|
bIsConstFunc: {
|
|
type: Boolean,
|
|
},
|
|
bIsCaseSensitive: {
|
|
type: Boolean,
|
|
},
|
|
VariableReference: {
|
|
type: VariableReferenceEntity,
|
|
},
|
|
SelfContextInfo: {
|
|
type: SymbolEntity,
|
|
},
|
|
DelegatePropertyName: {
|
|
type: String,
|
|
},
|
|
DelegateOwnerClass: {
|
|
type: ObjectReferenceEntity,
|
|
},
|
|
ComponentPropertyName: {
|
|
type: String,
|
|
},
|
|
EventReference: {
|
|
type: FunctionReferenceEntity,
|
|
},
|
|
FunctionReference: {
|
|
type: FunctionReferenceEntity,
|
|
},
|
|
CustomFunctionName: {
|
|
type: String,
|
|
},
|
|
TargetType: {
|
|
type: ObjectReferenceEntity,
|
|
},
|
|
MacroGraphReference: {
|
|
type: MacroGraphReferenceEntity,
|
|
},
|
|
Enum: {
|
|
type: ObjectReferenceEntity,
|
|
},
|
|
EnumEntries: {
|
|
type: [String],
|
|
inlined: true,
|
|
},
|
|
InputKey: {
|
|
type: SymbolEntity,
|
|
},
|
|
MaterialFunction: {
|
|
type: ObjectReferenceEntity,
|
|
},
|
|
bOverrideFunction: {
|
|
type: Boolean,
|
|
},
|
|
bInternalEvent: {
|
|
type: Boolean,
|
|
},
|
|
bConsumeInput: {
|
|
type: Boolean,
|
|
},
|
|
bExecuteWhenPaused: {
|
|
type: Boolean,
|
|
},
|
|
bOverrideParentBinding: {
|
|
type: Boolean,
|
|
},
|
|
bControl: {
|
|
type: Boolean,
|
|
},
|
|
bAlt: {
|
|
type: Boolean,
|
|
},
|
|
bShift: {
|
|
type: Boolean,
|
|
},
|
|
bCommand: {
|
|
type: Boolean,
|
|
},
|
|
CommentColor: {
|
|
type: LinearColorEntity,
|
|
},
|
|
bCommentBubbleVisible_InDetailsPanel: {
|
|
type: Boolean,
|
|
},
|
|
bColorCommentBubble: {
|
|
type: Boolean,
|
|
},
|
|
ProxyFactoryFunctionName: {
|
|
type: String,
|
|
},
|
|
ProxyFactoryClass: {
|
|
type: ObjectReferenceEntity,
|
|
},
|
|
ProxyClass: {
|
|
type: ObjectReferenceEntity,
|
|
},
|
|
R: {
|
|
type: Number,
|
|
},
|
|
G: {
|
|
type: Number,
|
|
},
|
|
StructType: {
|
|
type: ObjectReferenceEntity,
|
|
},
|
|
MaterialExpression: {
|
|
type: ObjectReferenceEntity,
|
|
},
|
|
MaterialExpressionComment: {
|
|
type: ObjectReferenceEntity,
|
|
},
|
|
MoveMode: {
|
|
type: SymbolEntity,
|
|
},
|
|
TimelineName: {
|
|
type: String,
|
|
},
|
|
TimelineGuid: {
|
|
type: GuidEntity,
|
|
},
|
|
SizeX: {
|
|
type: new MirroredEntity(ObjectEntity, "NodeWidth"),
|
|
},
|
|
SizeY: {
|
|
type: new MirroredEntity(ObjectEntity, "NodeHeight"),
|
|
},
|
|
Text: {
|
|
type: new MirroredEntity(ObjectEntity, "NodeComment"),
|
|
},
|
|
MaterialExpressionEditorX: {
|
|
type: new MirroredEntity(ObjectEntity, "NodePosX"),
|
|
},
|
|
MaterialExpressionEditorY: {
|
|
type: new MirroredEntity(ObjectEntity, "NodePosY"),
|
|
},
|
|
NodeTitle: {
|
|
type: String,
|
|
},
|
|
NodeTitleColor: {
|
|
type: LinearColorEntity,
|
|
},
|
|
PositionX: {
|
|
type: new MirroredEntity(ObjectEntity, "NodePosX"),
|
|
},
|
|
PositionY: {
|
|
type: new MirroredEntity(ObjectEntity, "NodePosY"),
|
|
},
|
|
PCGNode: {
|
|
type: ObjectReferenceEntity,
|
|
},
|
|
Operation: {
|
|
type: SymbolEntity,
|
|
},
|
|
NodePosX: {
|
|
type: IntegerEntity,
|
|
},
|
|
NodePosY: {
|
|
type: IntegerEntity,
|
|
},
|
|
NodeWidth: {
|
|
type: IntegerEntity,
|
|
},
|
|
NodeHeight: {
|
|
type: IntegerEntity,
|
|
},
|
|
Graph: {
|
|
type: ObjectReferenceEntity,
|
|
},
|
|
SubgraphInstance: {
|
|
type: String,
|
|
},
|
|
SettingsInterface: {
|
|
type: ObjectReferenceEntity,
|
|
},
|
|
InputPins: {
|
|
type: [ObjectReferenceEntity],
|
|
inlined: true,
|
|
},
|
|
OutputPins: {
|
|
type: [ObjectReferenceEntity],
|
|
inlined: true,
|
|
},
|
|
bExposeToLibrary: {
|
|
type: Boolean,
|
|
},
|
|
bCanRenameNode: {
|
|
type: Boolean,
|
|
},
|
|
bCommentBubblePinned: {
|
|
type: Boolean,
|
|
},
|
|
bCommentBubbleVisible: {
|
|
type: Boolean,
|
|
},
|
|
NodeComment: {
|
|
type: String,
|
|
},
|
|
AdvancedPinDisplay: {
|
|
type: IdentifierEntity,
|
|
},
|
|
EnabledState: {
|
|
type: IdentifierEntity,
|
|
},
|
|
NodeGuid: {
|
|
type: GuidEntity,
|
|
},
|
|
ErrorType: {
|
|
type: IntegerEntity,
|
|
},
|
|
ErrorMsg: {
|
|
type: String,
|
|
},
|
|
CustomProperties: {
|
|
type: [new Union(PinEntity, UnknownPinEntity)],
|
|
}
|
|
}
|
|
static {
|
|
this.cleanupAttributes(this.attributes)
|
|
}
|
|
static nameRegex = /^(\w+?)(?:_(\d+))?$/
|
|
static sequencerScriptingNameRegex = /\/Script\/SequencerScripting\.MovieSceneScripting(.+)Channel/
|
|
static customPropertyGrammar = Parsimmon.seq(
|
|
Parsimmon.regex(/CustomProperties\s+/),
|
|
Grammar.grammarFor(
|
|
undefined,
|
|
this.attributes.CustomProperties.type[0]
|
|
),
|
|
).map(([_0, pin]) => values => {
|
|
if (!values.CustomProperties) {
|
|
values.CustomProperties = []
|
|
}
|
|
values.CustomProperties.push(pin)
|
|
})
|
|
static inlinedArrayEntryGrammar = Parsimmon.seq(
|
|
Parsimmon.alt(
|
|
Grammar.symbolQuoted.map(v => [v, true]),
|
|
Grammar.symbol.map(v => [v, false]),
|
|
),
|
|
Grammar.regexMap(
|
|
new RegExp(`\\s*\\(\\s*(\\d+)\\s*\\)\\s*\\=\\s*`),
|
|
v => Number(v[1])
|
|
)
|
|
)
|
|
.chain(
|
|
/** @param {[[String, Boolean], Number]} param */
|
|
([[symbol, quoted], index]) =>
|
|
Grammar.grammarFor(this.attributes[symbol])
|
|
.map(currentValue =>
|
|
values => {
|
|
(values[symbol] ??= [])[index] = currentValue
|
|
Utility.objectSet(values, ["attributes", symbol, "quoted"], quoted, true)
|
|
if (!this.attributes[symbol]?.inlined) {
|
|
if (!values.attributes) {
|
|
IEntity.defineAttributes(values, {})
|
|
}
|
|
Utility.objectSet(values, ["attributes", symbol, "inlined"], true, true)
|
|
}
|
|
}
|
|
)
|
|
)
|
|
static grammar = this.createGrammar()
|
|
|
|
static createSubObjectGrammar() {
|
|
return Parsimmon.lazy(() =>
|
|
this.createGrammar()
|
|
.map(object =>
|
|
values => values[Configuration.subObjectAttributeNameFromEntity(object)] = object
|
|
)
|
|
)
|
|
}
|
|
|
|
static createGrammar() {
|
|
return Parsimmon.seq(
|
|
Parsimmon.regex(/Begin\s+Object/),
|
|
Parsimmon.seq(
|
|
Parsimmon.whitespace,
|
|
Parsimmon.alt(
|
|
this.customPropertyGrammar,
|
|
Grammar.createAttributeGrammar(this),
|
|
Grammar.createAttributeGrammar(this, Grammar.attributeNameQuoted, undefined, (obj, k, v) =>
|
|
Utility.objectSet(obj, ["attributes", ...k, "quoted"], true, true)
|
|
),
|
|
this.inlinedArrayEntryGrammar,
|
|
this.createSubObjectGrammar()
|
|
)
|
|
)
|
|
.map(([_0, entry]) => entry)
|
|
.many(),
|
|
Parsimmon.regex(/\s+End\s+Object/),
|
|
)
|
|
.map(([_0, attributes, _2]) => {
|
|
const values = {}
|
|
attributes.forEach(attributeSetter => attributeSetter(values))
|
|
return new this(values)
|
|
})
|
|
}
|
|
|
|
/** @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 Parsimmon.seq(
|
|
Parsimmon.optWhitespace,
|
|
this.createGrammar(),
|
|
Parsimmon.seq(
|
|
Parsimmon.whitespace,
|
|
this.createGrammar(),
|
|
)
|
|
.map(([_0, object]) => object)
|
|
.many(),
|
|
Parsimmon.optWhitespace
|
|
)
|
|
.map(([_0, first, remaining, _4]) => [first, ...remaining])
|
|
}
|
|
|
|
constructor(values = {}, suppressWarns = false) {
|
|
let keys = Object.keys(values)
|
|
if (keys.some(k => k.startsWith(Configuration.subObjectAttributeNamePrefix))) {
|
|
let subObjectsValues = keys
|
|
.filter(k => k.startsWith(Configuration.subObjectAttributeNamePrefix))
|
|
.reduce(
|
|
(acc, k) => {
|
|
acc[k] = values[k]
|
|
return acc
|
|
},
|
|
{}
|
|
)
|
|
// Reorder sub objects to be the first entries
|
|
values = {
|
|
...subObjectsValues,
|
|
...values,
|
|
}
|
|
}
|
|
super(values, suppressWarns)
|
|
/** @type {ObjectReferenceEntity} */ this.Class
|
|
/** @type {String} */ this.Name
|
|
/** @type {ObjectReferenceEntity} */ this.Archetype
|
|
/** @type {ObjectReferenceEntity} */ this.ExportPath
|
|
/** @type {ObjectReferenceEntity} */ this.ObjectRef
|
|
/** @type {ObjectReferenceEntity} */ this.BlueprintElementType
|
|
/** @type {ObjectReferenceEntity} */ this.BlueprintElementInstance
|
|
/** @type {null[]} */ this.PinTags
|
|
/** @type {String[]} */ this.PinNames
|
|
/** @type {SymbolEntity} */ this.AxisKey
|
|
/** @type {SymbolEntity} */ this.InputAxisKey
|
|
/** @type {Number} */ this.NumAdditionalInputs
|
|
/** @type {Boolean} */ this.bIsPureFunc
|
|
/** @type {Boolean} */ this.bIsConstFunc
|
|
/** @type {Boolean} */ this.bIsCaseSensitive
|
|
/** @type {VariableReferenceEntity} */ this.VariableReference
|
|
/** @type {SymbolEntity} */ this.SelfContextInfo
|
|
/** @type {String} */ this.DelegatePropertyName
|
|
/** @type {ObjectReferenceEntity} */ this.DelegateOwnerClass
|
|
/** @type {FunctionReferenceEntity} */ this.ComponentPropertyName
|
|
/** @type {FunctionReferenceEntity} */ this.EventReference
|
|
/** @type {FunctionReferenceEntity} */ this.FunctionReference
|
|
/** @type {String} */ this.CustomFunctionName
|
|
/** @type {ObjectReferenceEntity} */ this.TargetType
|
|
/** @type {MacroGraphReferenceEntity} */ this.MacroGraphReference
|
|
/** @type {ObjectReferenceEntity} */ this.Enum
|
|
/** @type {String[]} */ this.EnumEntries
|
|
/** @type {SymbolEntity} */ this.InputKey
|
|
/** @type {ObjectReferenceEntity} */ this.MaterialFunction
|
|
/** @type {Boolean} */ this.bOverrideFunction
|
|
/** @type {Boolean} */ this.bInternalEvent
|
|
/** @type {Boolean} */ this.bConsumeInput
|
|
/** @type {Boolean} */ this.bExecuteWhenPaused
|
|
/** @type {Boolean} */ this.bOverrideParentBinding
|
|
/** @type {Boolean} */ this.bControl
|
|
/** @type {Boolean} */ this.bAlt
|
|
/** @type {Boolean} */ this.bShift
|
|
/** @type {Boolean} */ this.bCommand
|
|
/** @type {LinearColorEntity} */ this.CommentColor
|
|
/** @type {Boolean} */ this.bCommentBubbleVisible_InDetailsPanel
|
|
/** @type {Boolean} */ this.bColorCommentBubble
|
|
/** @type {String} */ this.ProxyFactoryFunctionName
|
|
/** @type {ObjectReferenceEntity} */ this.ProxyFactoryClass
|
|
/** @type {ObjectReferenceEntity} */ this.ProxyClass
|
|
/** @type {Number} */ this.R
|
|
/** @type {Number} */ this.G
|
|
/** @type {ObjectReferenceEntity} */ this.StructType
|
|
/** @type {ObjectReferenceEntity} */ this.MaterialExpression
|
|
/** @type {ObjectReferenceEntity} */ this.MaterialExpressionComment
|
|
/** @type {SymbolEntity} */ this.MoveMode
|
|
/** @type {String} */ this.TimelineName
|
|
/** @type {GuidEntity} */ this.TimelineGuid
|
|
/** @type {MirroredEntity} */ this.SizeX
|
|
/** @type {MirroredEntity} */ this.SizeY
|
|
/** @type {MirroredEntity} */ this.Text
|
|
/** @type {MirroredEntity} */ this.MaterialExpressionEditorX
|
|
/** @type {MirroredEntity} */ this.MaterialExpressionEditorY
|
|
/** @type {String} */ this.NodeTitle
|
|
/** @type {LinearColorEntity} */ this.NodeTitleColor
|
|
/** @type {MirroredEntity} */ this.PositionX
|
|
/** @type {MirroredEntity} */ this.PositionY
|
|
/** @type {ObjectReferenceEntity} */ this.PCGNode
|
|
/** @type {String} */ this.Operation
|
|
/** @type {IntegerEntity} */ this.NodePosX
|
|
/** @type {IntegerEntity} */ this.NodePosY
|
|
/** @type {IntegerEntity} */ this.NodeWidth
|
|
/** @type {IntegerEntity} */ this.NodeHeight
|
|
/** @type {ObjectReferenceEntity} */ this.Graph
|
|
/** @type {String} */ this.SubgraphInstance
|
|
/** @type {ObjectReferenceEntity} */ this.SettingsInterface
|
|
/** @type {ObjectReferenceEntity[]} */ this.InputPins
|
|
/** @type {ObjectReferenceEntity[]} */ this.OutputPins
|
|
/** @type {Boolean} */ this.bExposeToLibrary
|
|
/** @type {Boolean} */ this.bCanRenameNode
|
|
/** @type {Boolean} */ this.bCommentBubblePinned
|
|
/** @type {Boolean} */ this.bCommentBubbleVisible
|
|
/** @type {String} */ this.Text
|
|
/** @type {String} */ this.NodeComment
|
|
/** @type {IdentifierEntity} */ this.AdvancedPinDisplay
|
|
/** @type {IdentifierEntity} */ this.EnabledState
|
|
/** @type {GuidEntity} */ this.NodeGuid
|
|
/** @type {IntegerEntity} */ this.ErrorType
|
|
/** @type {String} */ this.ErrorMsg
|
|
/** @type {(PinEntity | UnknownPinEntity)[]} */ this.CustomProperties
|
|
|
|
// Legacy nodes cleanup
|
|
if (this["Pins"] instanceof Array) {
|
|
this["Pins"]
|
|
.forEach(
|
|
/** @param {ObjectReferenceEntity} objectReference */
|
|
objectReference => {
|
|
const pinObject = this[Configuration.subObjectAttributeNameFromReference(objectReference, true)]
|
|
if (pinObject) {
|
|
const pinEntity = PinEntity.fromLegacyObject(pinObject)
|
|
pinEntity.LinkedTo = []
|
|
this.getCustomproperties(true).push(pinEntity)
|
|
}
|
|
})
|
|
delete this["Pins"]
|
|
}
|
|
this.Class?.sanitize()
|
|
if (this.MacroGraphReference) {
|
|
this.MacroGraphReference.MacroGraph?.sanitize()
|
|
this.MacroGraphReference.GraphBlueprint?.sanitize()
|
|
}
|
|
/** @type {ObjectEntity} */
|
|
const materialSubobject = this.getMaterialSubobject()
|
|
if (materialSubobject) {
|
|
const obj = materialSubobject
|
|
obj.SizeX && (obj.SizeX.getter = () => this.NodeWidth)
|
|
obj.SizeY && (obj.SizeY.getter = () => this.NodeHeight)
|
|
obj.Text && (obj.Text.getter = () => this.NodeComment)
|
|
obj.MaterialExpressionEditorX && (obj.MaterialExpressionEditorX.getter = () => this.NodePosX)
|
|
obj.MaterialExpressionEditorY && (obj.MaterialExpressionEditorY.getter = () => this.NodePosY)
|
|
}
|
|
/** @type {ObjectEntity} */
|
|
const pcgObject = this.getPcgSubobject()
|
|
if (pcgObject) {
|
|
pcgObject.PositionX && (pcgObject.PositionX.getter = () => this.NodePosX)
|
|
pcgObject.PositionY && (pcgObject.PositionY.getter = () => this.NodePosY)
|
|
}
|
|
let inputIndex = 0
|
|
let outputIndex = 0
|
|
this.CustomProperties?.forEach((pinEntity, i) => {
|
|
pinEntity.objectEntity = this
|
|
pinEntity.pinIndex = pinEntity.isInput()
|
|
? inputIndex++
|
|
: pinEntity.isOutput()
|
|
? outputIndex++
|
|
: i
|
|
})
|
|
}
|
|
|
|
getClass() {
|
|
return (this.Class?.path ? this.Class.path : this.Class?.type)
|
|
?? (this.ExportPath?.path ? this.ExportPath.path : this.ExportPath?.type)
|
|
?? ""
|
|
}
|
|
|
|
getType() {
|
|
let classValue = this.getClass()
|
|
if (this.MacroGraphReference?.MacroGraph?.path) {
|
|
return this.MacroGraphReference.MacroGraph.path
|
|
}
|
|
if (this.MaterialExpression) {
|
|
return this.MaterialExpression.type
|
|
}
|
|
return classValue
|
|
}
|
|
|
|
getObjectName(dropCounter = false) {
|
|
if (dropCounter) {
|
|
return this.getNameAndCounter()[0]
|
|
}
|
|
return this.Name
|
|
}
|
|
|
|
/** @returns {[String, Number]} */
|
|
getNameAndCounter() {
|
|
const result = this.getObjectName(false).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]
|
|
}
|
|
|
|
getCounter() {
|
|
return this.getNameAndCounter()[1]
|
|
}
|
|
|
|
getNodeWidth() {
|
|
return this.NodeWidth
|
|
?? this.isComment() ? Configuration.defaultCommentWidth : undefined
|
|
}
|
|
|
|
/** @param {Number} value */
|
|
setNodeWidth(value) {
|
|
if (!this.NodeWidth) {
|
|
this.NodeWidth = new IntegerEntity()
|
|
}
|
|
this.NodeWidth.value = value
|
|
}
|
|
|
|
getNodeHeight() {
|
|
return this.NodeHeight
|
|
?? this.isComment() ? Configuration.defaultCommentHeight : undefined
|
|
}
|
|
|
|
/** @param {Number} value */
|
|
setNodeHeight(value) {
|
|
if (!this.NodeHeight) {
|
|
this.NodeHeight = new IntegerEntity()
|
|
}
|
|
this.NodeHeight.value = value
|
|
}
|
|
|
|
getNodePosX() {
|
|
return this.NodePosX?.value ?? 0
|
|
}
|
|
|
|
/** @param {Number} value */
|
|
setNodePosX(value) {
|
|
if (!this.NodePosX) {
|
|
this.NodePosX = new IntegerEntity()
|
|
}
|
|
this.NodePosX.value = Math.round(value)
|
|
}
|
|
|
|
getNodePosY() {
|
|
return this.NodePosY?.value ?? 0
|
|
}
|
|
|
|
/** @param {Number} value */
|
|
setNodePosY(value) {
|
|
if (!this.NodePosY) {
|
|
this.NodePosY = new IntegerEntity()
|
|
}
|
|
this.NodePosY.value = Math.round(value)
|
|
}
|
|
|
|
getCustomproperties(canCreate = false) {
|
|
if (canCreate && !this.CustomProperties) {
|
|
this.CustomProperties = []
|
|
}
|
|
return this.CustomProperties ?? []
|
|
}
|
|
|
|
/** @returns {PinEntity[]} */
|
|
getPinEntities() {
|
|
return this.getCustomproperties().filter(v => v.constructor === PinEntity)
|
|
}
|
|
|
|
switchTarget() {
|
|
const switchMatch = this.getClass().match(Configuration.switchTargetPattern)
|
|
if (switchMatch) {
|
|
return switchMatch[1]
|
|
}
|
|
}
|
|
|
|
isEvent() {
|
|
switch (this.getClass()) {
|
|
case Configuration.paths.componentBoundEvent:
|
|
case Configuration.paths.customEvent:
|
|
case Configuration.paths.event:
|
|
case Configuration.paths.inputAxisKeyEvent:
|
|
case Configuration.paths.inputVectorAxisEvent:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
isComment() {
|
|
switch (this.getClass()) {
|
|
case Configuration.paths.comment:
|
|
case Configuration.paths.materialGraphNodeComment:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
isMaterial() {
|
|
return this.getClass() === Configuration.paths.materialGraphNode
|
|
}
|
|
|
|
/** @return {ObjectEntity} */
|
|
getMaterialSubobject() {
|
|
const expression = this.MaterialExpression ?? this.MaterialExpressionComment
|
|
return expression
|
|
? this[Configuration.subObjectAttributeNameFromReference(expression, true)]
|
|
: null
|
|
}
|
|
|
|
isPcg() {
|
|
return this.getClass() === Configuration.paths.pcgEditorGraphNode
|
|
|| this.getPcgSubobject()
|
|
}
|
|
|
|
/** @return {ObjectEntity} */
|
|
getPcgSubobject() {
|
|
const node = this.PCGNode
|
|
return node
|
|
? this[Configuration.subObjectAttributeNameFromReference(node, true)]
|
|
: null
|
|
}
|
|
|
|
/** @return {ObjectEntity} */
|
|
getSettingsObject() {
|
|
const settings = this.SettingsInterface
|
|
return settings
|
|
? this[Configuration.subObjectAttributeNameFromReference(settings, true)]
|
|
: null
|
|
}
|
|
|
|
/** @return {ObjectEntity} */
|
|
getSubgraphObject() {
|
|
const node = this.SubgraphInstance
|
|
return node
|
|
? this[Configuration.subObjectAttributeNameFromName(node)]
|
|
: null
|
|
}
|
|
|
|
isDevelopmentOnly() {
|
|
const nodeClass = this.getClass()
|
|
return this.EnabledState?.toString() === "DevelopmentOnly"
|
|
|| nodeClass.includes("Debug", Math.max(0, nodeClass.lastIndexOf(".")))
|
|
}
|
|
|
|
getHIDAttribute() {
|
|
return this.InputKey ?? this.AxisKey ?? this.InputAxisKey
|
|
}
|
|
|
|
getDelegatePin() {
|
|
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.componentBoundEvent:
|
|
return `${Utility.formatStringName(this.DelegatePropertyName)} (${this.ComponentPropertyName})`
|
|
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.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.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.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.slateBlueprintLibrary:
|
|
case Configuration.paths.kismetMathLibrary:
|
|
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.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
|
|
}
|
|
|
|
nodeIcon() {
|
|
if (this.isMaterial() || this.isPcg()) {
|
|
return undefined
|
|
}
|
|
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
|
|
}
|
|
|
|
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.CustomProperties.push(newPin)
|
|
return newPin
|
|
}
|
|
}
|
|
}
|
|
}
|