mirror of
https://github.com/barsdeveloper/ueblueprint.git
synced 2026-02-04 08:50:33 +08:00
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:
@@ -10,6 +10,7 @@
|
||||
* inlined?: Boolean,
|
||||
* quoted?: Boolean,
|
||||
* silent?: Boolean,
|
||||
* uninitialized?: Boolean,
|
||||
* predicate?: (value: T) => Boolean,
|
||||
* }} AttributeInfoSource
|
||||
*/
|
||||
@@ -27,6 +28,7 @@ export default class AttributeInfo {
|
||||
inlined: false, // The key is a subobject or array and printed as inlined (A.B=123, A(0)=123)
|
||||
quoted: false, // Key is serialized with quotes
|
||||
silent: false, // Do not serialize if default
|
||||
uninitialized: false, // Do not initialize with default
|
||||
}
|
||||
|
||||
/** @param {AttributeInfoSource<T>} source */
|
||||
@@ -40,6 +42,7 @@ export default class AttributeInfo {
|
||||
this.inlined = source.inlined
|
||||
this.quoted = source.quoted
|
||||
this.silent = source.silent
|
||||
this.uninitialized = source.uninitialized
|
||||
this.predicate = source.predicate
|
||||
if (this.type === Array && this.default instanceof Array && this.default.length > 0) {
|
||||
this.type = this.default
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
|
||||
export default class Base64ObjectsEncoded extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
value: AttributeInfo.createType(String),
|
||||
objects: new AttributeInfo({
|
||||
ignored: true,
|
||||
}),
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
super(values)
|
||||
/** @type {String} */this.value
|
||||
/** @type {ObjectEntity[]} */this.objects
|
||||
}
|
||||
|
||||
decode() {
|
||||
|
||||
}
|
||||
}
|
||||
88
js/entity/BlueprintEntity.js
Normal file
88
js/entity/BlueprintEntity.js
Normal file
@@ -0,0 +1,88 @@
|
||||
import Configuration from "../Configuration.js"
|
||||
import Utility from "../Utility.js"
|
||||
import ObjectEntity from "./ObjectEntity.js"
|
||||
|
||||
export default class BlueprintEntity extends ObjectEntity {
|
||||
|
||||
/** @type {Map<String, Number>} */
|
||||
#objectEntitiesNameCounter = new Map()
|
||||
|
||||
/** @type {ObjectEntity[]}" */
|
||||
#objectEntities = []
|
||||
get objectEntities() {
|
||||
return this.#objectEntities
|
||||
}
|
||||
|
||||
/** @param {ObjectEntity} entity */
|
||||
getHomonymObjectEntity(entity) {
|
||||
const name = entity.getObjectName(false)
|
||||
return this.#objectEntities.find(entity => entity.getObjectName() == name)
|
||||
}
|
||||
|
||||
/** @param {String} name */
|
||||
takeFreeName(name) {
|
||||
name = name.replace(/_\d+$/, "")
|
||||
const counter = (this.#objectEntitiesNameCounter.get(name) ?? -1) + 1
|
||||
this.#objectEntitiesNameCounter.set(name, counter)
|
||||
return Configuration.nodeTitle(name, counter)
|
||||
}
|
||||
|
||||
/** @param {ObjectEntity} entity */
|
||||
addObjectEntity(entity) {
|
||||
if (!this.#objectEntities.includes(entity)) {
|
||||
this.#objectEntities.push(entity)
|
||||
const [name, counter] = entity.getNameAndCounter()
|
||||
this.#objectEntitiesNameCounter.set(
|
||||
name,
|
||||
Math.max((this.#objectEntitiesNameCounter.get(name) ?? 0), counter)
|
||||
)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/** @param {ObjectEntity} entity */
|
||||
removeObjectEntity(entity) {
|
||||
const index = this.#objectEntities.indexOf(entity)
|
||||
if (index >= 0) {
|
||||
const last = this.#objectEntities.pop()
|
||||
if (index < this.#objectEntities.length) {
|
||||
this.#objectEntities[index] = last
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/** @param {ObjectEntity} entity */
|
||||
mergeWith(entity) {
|
||||
if (!entity.ScriptVariables || entity.ScriptVariables.length === 0) {
|
||||
return this
|
||||
}
|
||||
if (!this.ScriptVariables || this.ScriptVariables.length === 0) {
|
||||
this.ScriptVariables = entity.ScriptVariables
|
||||
}
|
||||
let scriptVariables = Utility.mergeArrays(
|
||||
this.ScriptVariables,
|
||||
entity.ScriptVariables,
|
||||
(l, r) => l.OriginalChangeId.value == r.OriginalChangeId.value
|
||||
)
|
||||
if (scriptVariables.length === this.ScriptVariables.length) {
|
||||
return this
|
||||
}
|
||||
const entries = scriptVariables.concat(scriptVariables).map((v, i) => {
|
||||
const name = Configuration.subObjectAttributeNameFromReference(v.ScriptVariable, i >= scriptVariables.length)
|
||||
return [
|
||||
name,
|
||||
this[name] ?? entity[name]
|
||||
]
|
||||
})
|
||||
entries.push(
|
||||
...Object.entries(this).filter(([k, v]) =>
|
||||
!k.startsWith(Configuration.subObjectAttributeNamePrefix)
|
||||
&& k !== "ExportedNodes"
|
||||
)
|
||||
)
|
||||
return new BlueprintEntity(Object.fromEntries(entries))
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,19 @@ export default class IEntity extends Serializable {
|
||||
lookbehind: new AttributeInfo({
|
||||
default: /** @type {String | Union<String[]>} */(""),
|
||||
ignored: true,
|
||||
uninitialized: true,
|
||||
}),
|
||||
}
|
||||
|
||||
/** @type {String[]} */
|
||||
#_keys
|
||||
get _keys() {
|
||||
return this.#_keys
|
||||
}
|
||||
set _keys(keys) {
|
||||
this.#_keys = keys
|
||||
}
|
||||
|
||||
constructor(values = {}, suppressWarns = false) {
|
||||
super()
|
||||
const Self = /** @type {typeof IEntity} */(this.constructor)
|
||||
@@ -110,7 +120,7 @@ export default class IEntity extends Serializable {
|
||||
assignAttribute(Utility.sanitize(value, /** @type {AttributeConstructor<Attribute>} */(defaultType)))
|
||||
continue // We have a value, need nothing more
|
||||
}
|
||||
if (defaultValue !== undefined) {
|
||||
if (defaultValue !== undefined && !AttributeInfo.getAttribute(values, key, "uninitialized", Self)) {
|
||||
assignAttribute(defaultValue)
|
||||
}
|
||||
}
|
||||
@@ -156,6 +166,44 @@ export default class IEntity extends Serializable {
|
||||
object.attributes = attributes
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} attribute
|
||||
* @param {(v: any) => void} callback
|
||||
*/
|
||||
listenAttribute(attribute, callback) {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(this, attribute)
|
||||
const setter = descriptor.set
|
||||
if (setter) {
|
||||
descriptor.set = v => {
|
||||
setter(v)
|
||||
callback(v)
|
||||
}
|
||||
Object.defineProperties(this, { [attribute]: descriptor })
|
||||
} else if (descriptor.value) {
|
||||
Object.defineProperties(this, {
|
||||
["#" + attribute]: {
|
||||
value: descriptor.value,
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
},
|
||||
[attribute]: {
|
||||
enumerable: true,
|
||||
get() {
|
||||
return this["#" + attribute]
|
||||
},
|
||||
set(v) {
|
||||
if (v == this["#" + attribute]) {
|
||||
return
|
||||
}
|
||||
callback(v)
|
||||
this["#" + attribute] = v
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
getLookbehind() {
|
||||
let lookbehind = this.lookbehind ?? AttributeInfo.getAttribute(this, "lookbehind", "default")
|
||||
lookbehind = lookbehind instanceof Union ? lookbehind.values[0] : lookbehind
|
||||
|
||||
@@ -37,4 +37,8 @@ export default class InvariantTextEntity extends IEntity {
|
||||
super(values)
|
||||
/** @type {String} */ this.value
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import Configuration from "../Configuration.js"
|
||||
import Utility from "../Utility.js"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
@@ -7,11 +6,6 @@ import IEntity from "./IEntity.js"
|
||||
|
||||
export default class ObjectReferenceEntity extends IEntity {
|
||||
|
||||
static #quoteSymbols = [
|
||||
[`'"`, Grammar.Regex.InsideString.source],
|
||||
[`'`, Grammar.Regex.InsideSingleQuotedString.source],
|
||||
[`"`, Grammar.Regex.InsideString.source]
|
||||
]
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
type: new AttributeInfo({
|
||||
@@ -22,14 +16,15 @@ export default class ObjectReferenceEntity extends IEntity {
|
||||
default: "",
|
||||
serialized: true,
|
||||
}),
|
||||
delim: new AttributeInfo({
|
||||
_full: new AttributeInfo({
|
||||
ignored: true,
|
||||
}),
|
||||
}
|
||||
static quoted = Parsernostrum.regArray(new RegExp(
|
||||
this.#quoteSymbols.map(([delim, parser]) =>
|
||||
delim + "(" + parser + ")" + delim.split("").reverse().join("")).join("|")
|
||||
)).map(([_0, a, b, c]) => a ?? b ?? c)
|
||||
`'"(${Grammar.Regex.InsideString.source})"'`
|
||||
+ "|"
|
||||
+ `'(${Grammar.Regex.InsideSingleQuotedString.source})'`
|
||||
)).map(([_0, a, b]) => a ?? b)
|
||||
static path = this.quoted.getParser().parser.regexp.source + "|" + Grammar.Regex.Path.source
|
||||
static typeReference = Parsernostrum.reg(
|
||||
new RegExp(Grammar.Regex.Path.source + "|" + Grammar.symbol.getParser().regexp.source)
|
||||
@@ -37,28 +32,32 @@ export default class ObjectReferenceEntity extends IEntity {
|
||||
static fullReferenceGrammar = Parsernostrum.regArray(
|
||||
new RegExp(
|
||||
"(" + this.typeReference.getParser().regexp.source + ")"
|
||||
+ /\s*/.source
|
||||
+ "(?:" + this.quoted.getParser().parser.regexp.source + ")"
|
||||
)
|
||||
).map(([_0, type, ...path]) => new this({
|
||||
type,
|
||||
path: path.find(v => v),
|
||||
delim: this.#quoteSymbols[path.findIndex(v => v)]?.[0] ?? "",
|
||||
}))
|
||||
).map(([_full, type, ...path]) => new this({ type, path: path.find(v => v), _full }))
|
||||
static fullReferenceSerializedGrammar = Parsernostrum.regArray(
|
||||
new RegExp(
|
||||
"(" + this.typeReference.getParser().regexp.source + ")"
|
||||
+ /\s*/.source
|
||||
+ `'(` + Grammar.Regex.InsideSingleQuotedString.source + `)'`
|
||||
)
|
||||
).map(([_0, type, ...path]) => new this({
|
||||
type,
|
||||
path: path.find(v => v),
|
||||
delim: "'",
|
||||
}))
|
||||
static typeReferenceGrammar = this.typeReference.map(v => new this({ type: v, path: "" }))
|
||||
).map(([_full, type, ...path]) => new this({ type, path: path.find(v => v), _full }))
|
||||
static typeReferenceGrammar = this.typeReference.map(v => new this({ type: v, path: "", _full: v }))
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
constructor(values = {}) {
|
||||
if (values.constructor === String) {
|
||||
values = {
|
||||
path: values
|
||||
}
|
||||
}
|
||||
super(values)
|
||||
if (!values._full || values._full.length === 0) {
|
||||
this._full = `"${this.type + (this.path ? (`'${this.path}'`) : "")}"`
|
||||
}
|
||||
/** @type {String} */ this.type
|
||||
/** @type {String} */ this.path
|
||||
}
|
||||
|
||||
static createGrammar() {
|
||||
return Parsernostrum.alt(
|
||||
Parsernostrum.seq(
|
||||
@@ -68,33 +67,21 @@ export default class ObjectReferenceEntity extends IEntity {
|
||||
this.typeReferenceGrammar,
|
||||
),
|
||||
Parsernostrum.str('"'),
|
||||
).map(([_0, objectReference, _1]) => objectReference),
|
||||
this.fullReferenceGrammar.map(v => (Utility.objectSet(v, ["attributes", "type", "serialized"], false), v)),
|
||||
this.typeReferenceGrammar.map(v => (Utility.objectSet(v, ["attributes", "type", "serialized"], false), v)),
|
||||
).map(([_0, objectReference, _1]) => (objectReference._full = `"${objectReference._full}"`, objectReference)),
|
||||
this.fullReferenceGrammar,
|
||||
this.typeReferenceGrammar,
|
||||
)
|
||||
}
|
||||
|
||||
constructor(values = {}) {
|
||||
if (values.constructor === String) {
|
||||
values = {
|
||||
path: values
|
||||
}
|
||||
}
|
||||
super(values)
|
||||
/** @type {String} */ this.type
|
||||
/** @type {String} */ this.path
|
||||
/** @type {String} */ this.delim
|
||||
}
|
||||
|
||||
static createNoneInstance() {
|
||||
return new ObjectReferenceEntity({ type: "None", path: "" })
|
||||
}
|
||||
|
||||
getName() {
|
||||
return Utility.getNameFromPath(this.path.replace(/_C$/, ""))
|
||||
getName(dropCounter = false) {
|
||||
return Utility.getNameFromPath(this.path.replace(/_C$/, ""), dropCounter)
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.type + (this.path ? (this.delim + this.path + this.delim.split("").reverse().join("")) : "")
|
||||
return this._full
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Configuration from "../Configuration.js"
|
||||
import Utility from "../Utility.js"
|
||||
import pinColor from "../decoding/pinColor.js"
|
||||
import pinTitle from "../decoding/pinTitle.js"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import ByteEntity from "./ByteEntity.js"
|
||||
@@ -11,6 +12,7 @@ import GuidEntity from "./GuidEntity.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
import Integer64Entity from "./Integer64Entity.js"
|
||||
import IntegerEntity from "./IntegerEntity.js"
|
||||
import InvariantTextEntity from "./InvariantTextEntity.js"
|
||||
import LinearColorEntity from "./LinearColorEntity.js"
|
||||
import LocalizedTextEntity from "./LocalizedTextEntity.js"
|
||||
import ObjectReferenceEntity from "./ObjectReferenceEntity.js"
|
||||
@@ -20,9 +22,11 @@ import RBSerializationVector2DEntity from "./RBSerializationVector2DEntity.js"
|
||||
import RotatorEntity from "./RotatorEntity.js"
|
||||
import SimpleSerializationRotatorEntity from "./SimpleSerializationRotatorEntity.js"
|
||||
import SimpleSerializationVector2DEntity from "./SimpleSerializationVector2DEntity.js"
|
||||
import SimpleSerializationVector4DEntity from "./SimpleSerializationVector4DEntity.js"
|
||||
import SimpleSerializationVectorEntity from "./SimpleSerializationVectorEntity.js"
|
||||
import Union from "./Union.js"
|
||||
import Vector2DEntity from "./Vector2DEntity.js"
|
||||
import Vector4DEntity from "./Vector4DEntity.js"
|
||||
import VectorEntity from "./VectorEntity.js"
|
||||
|
||||
/** @template {TerminalAttribute} T */
|
||||
@@ -33,6 +37,7 @@ export default class PinEntity extends IEntity {
|
||||
[Configuration.paths.rotator]: RotatorEntity,
|
||||
[Configuration.paths.vector]: VectorEntity,
|
||||
[Configuration.paths.vector2D]: Vector2DEntity,
|
||||
[Configuration.paths.vector4f]: Vector4DEntity,
|
||||
"bool": Boolean,
|
||||
"byte": ByteEntity,
|
||||
"enum": EnumEntity,
|
||||
@@ -49,6 +54,8 @@ export default class PinEntity extends IEntity {
|
||||
[Configuration.paths.rotator]: SimpleSerializationRotatorEntity,
|
||||
[Configuration.paths.vector]: SimpleSerializationVectorEntity,
|
||||
[Configuration.paths.vector2D]: SimpleSerializationVector2DEntity,
|
||||
[Configuration.paths.vector3f]: SimpleSerializationVectorEntity,
|
||||
[Configuration.paths.vector4f]: SimpleSerializationVector4DEntity,
|
||||
}
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
@@ -68,7 +75,7 @@ export default class PinEntity extends IEntity {
|
||||
default: () => new GuidEntity()
|
||||
}),
|
||||
PinName: AttributeInfo.createValue(""),
|
||||
PinFriendlyName: AttributeInfo.createType(new Union(LocalizedTextEntity, FormatTextEntity, String)),
|
||||
PinFriendlyName: AttributeInfo.createType(new Union(LocalizedTextEntity, FormatTextEntity, InvariantTextEntity, String)),
|
||||
PinToolTip: AttributeInfo.createType(String),
|
||||
Direction: AttributeInfo.createType(String),
|
||||
PinType: new AttributeInfo({
|
||||
@@ -139,8 +146,8 @@ export default class PinEntity extends IEntity {
|
||||
}
|
||||
|
||||
getType() {
|
||||
const category = this.PinType.PinCategory
|
||||
if (category === "struct" || category === "object") {
|
||||
const category = this.PinType.PinCategory.toLocaleLowerCase()
|
||||
if (category === "struct" || category === "class" || category === "object" || category === "type") {
|
||||
return this.PinType.PinSubCategoryObject.path
|
||||
}
|
||||
if (this.isEnum()) {
|
||||
@@ -198,21 +205,8 @@ export default class PinEntity extends IEntity {
|
||||
: entity
|
||||
}
|
||||
|
||||
pinDisplayName() {
|
||||
let result = this.PinFriendlyName
|
||||
? this.PinFriendlyName.toString()
|
||||
: Utility.formatStringName(this.PinName ?? "")
|
||||
let match
|
||||
if (
|
||||
this.PinToolTip
|
||||
// Match up until the first \n excluded or last character
|
||||
&& (match = this.PinToolTip.match(/\s*(.+?(?=\n)|.+\S)\s*/))
|
||||
) {
|
||||
if (match[1].toLowerCase() === result.toLowerCase()) {
|
||||
return match[1] // In case they match, then keep the case of the PinToolTip
|
||||
}
|
||||
}
|
||||
return result
|
||||
pinTitle() {
|
||||
return pinTitle(this)
|
||||
}
|
||||
|
||||
/** @param {PinEntity} other */
|
||||
@@ -309,18 +303,7 @@ export default class PinEntity extends IEntity {
|
||||
return this.PinType.PinSubCategoryObject.path
|
||||
}
|
||||
|
||||
/** @return {CSSResult} */
|
||||
pinColor() {
|
||||
if (this.PinType.PinCategory == "mask") {
|
||||
const result = Configuration.pinColor[this.PinType.PinSubCategory]
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
} else if (this.PinType.PinCategory == "optional") {
|
||||
return Configuration.pinColorMaterial
|
||||
}
|
||||
return Configuration.pinColor[this.getType()]
|
||||
?? Configuration.pinColor[this.PinType.PinCategory.toLowerCase()]
|
||||
?? Configuration.pinColor["default"]
|
||||
return pinColor(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ export default class PinReferenceEntity extends IEntity {
|
||||
|
||||
static createGrammar() {
|
||||
return Parsernostrum.seq(
|
||||
PathSymbolEntity.createGrammar(),
|
||||
PathSymbolEntity.grammar,
|
||||
Parsernostrum.whitespace,
|
||||
GuidEntity.createGrammar()
|
||||
GuidEntity.grammar
|
||||
).map(
|
||||
([objectName, _1, pinGuid]) => new this({
|
||||
objectName: objectName,
|
||||
|
||||
@@ -15,7 +15,7 @@ export default class RBSerializationVector2DEntity extends Vector2DEntity {
|
||||
X: Number(x),
|
||||
Y: Number(y),
|
||||
})),
|
||||
Vector2DEntity.createGrammar()
|
||||
Vector2DEntity.grammar
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
25
js/entity/ScriptVariableEntity.js
Normal file
25
js/entity/ScriptVariableEntity.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import GuidEntity from "./GuidEntity.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
import ObjectReferenceEntity from "./ObjectReferenceEntity.js"
|
||||
|
||||
export default class ScriptVariableEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
ScriptVariable: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
OriginalChangeId: AttributeInfo.createType(GuidEntity),
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Grammar.createEntityGrammar(this)
|
||||
}
|
||||
|
||||
constructor(values = {}, suppressWarns = false) {
|
||||
super(values, suppressWarns)
|
||||
/** @type {ObjectReferenceEntity} */ this.ScriptVariable
|
||||
/** @type {GuidEntity} */ this.OriginalChangeId
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ export default class SimpleSerializationRotatorEntity extends RotatorEntity {
|
||||
P: Number(p),
|
||||
Y: Number(y),
|
||||
})),
|
||||
RotatorEntity.createGrammar()
|
||||
RotatorEntity.grammar
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export default class SimpleSerializationVector2DEntity extends Vector2DEntity {
|
||||
X: Number(x),
|
||||
Y: Number(y),
|
||||
})),
|
||||
Vector2DEntity.createGrammar()
|
||||
Vector2DEntity.grammar
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
29
js/entity/SimpleSerializationVector4DEntity.js
Normal file
29
js/entity/SimpleSerializationVector4DEntity.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import Vector4DEntity from "./Vector4DEntity.js"
|
||||
|
||||
export default class SimpleSerializationVector4DEntity extends Vector4DEntity {
|
||||
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
const number = Parsernostrum.number.getParser().parser.regexp.source
|
||||
return Parsernostrum.alt(
|
||||
Parsernostrum.regArray(new RegExp(
|
||||
"(" + number + ")"
|
||||
+ "\\s*,\\s*"
|
||||
+ "(" + number + ")"
|
||||
+ "\\s*,\\s*"
|
||||
+ "(" + number + ")"
|
||||
+ "\\s*,\\s*"
|
||||
+ "(" + number + ")"
|
||||
))
|
||||
.map(([_0, x, y, z, w]) => new this({
|
||||
X: Number(x),
|
||||
Y: Number(y),
|
||||
Z: Number(z),
|
||||
W: Number(w),
|
||||
})),
|
||||
Vector4DEntity.grammar
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ export default class SimpleSerializationVectorEntity extends VectorEntity {
|
||||
Y: Number(y),
|
||||
Z: Number(z),
|
||||
})),
|
||||
VectorEntity.createGrammar()
|
||||
VectorEntity.grammar
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
44
js/entity/Vector4DEntity.js
Normal file
44
js/entity/Vector4DEntity.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
|
||||
export default class Vector4DEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
X: new AttributeInfo({
|
||||
default: 0,
|
||||
expected: true,
|
||||
}),
|
||||
Y: new AttributeInfo({
|
||||
default: 0,
|
||||
expected: true,
|
||||
}),
|
||||
Z: new AttributeInfo({
|
||||
default: 0,
|
||||
expected: true,
|
||||
}),
|
||||
W: new AttributeInfo({
|
||||
default: 0,
|
||||
expected: true,
|
||||
}),
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Grammar.createEntityGrammar(Vector4DEntity, false)
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
super(values)
|
||||
/** @type {Number} */ this.X
|
||||
/** @type {Number} */ this.Y
|
||||
/** @type {Number} */ this.Z
|
||||
/** @type {Number} */ this.W
|
||||
}
|
||||
|
||||
/** @returns {[Number, Number, Number, Number]} */
|
||||
toArray() {
|
||||
return [this.X, this.Y, this.Z, this.W]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user