Niagara and Metasound nodes WIP

* Keep track of entities

* Fix renaming

* Niagara variables wip

* Several niagara decode and test

* Move nodeTemplate code to dedicated file, self node added

* Move node decoding functions to dedicated files

* Move pin decoding logic to dedicated files

* Accept space separated keys in objects

* Build

* Prevent a crash in case of incomplete object

* Avoid creating objects unnecessarily

* types formatting

* Initial metasound style

* Common pcg nodes colors

* Fix string serialization

* Metasound new styles and fixes

* More metasound styles and colors

* WIP

* Several fixes

* More tests and fixes

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

View File

@@ -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

View File

@@ -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() {
}
}

View 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))
}
}

View File

@@ -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

View File

@@ -37,4 +37,8 @@ export default class InvariantTextEntity extends IEntity {
super(values)
/** @type {String} */ this.value
}
toString() {
return this.value
}
}

View File

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

View File

@@ -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
}
}

View File

@@ -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)
}
}

View File

@@ -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,

View File

@@ -15,7 +15,7 @@ export default class RBSerializationVector2DEntity extends Vector2DEntity {
X: Number(x),
Y: Number(y),
})),
Vector2DEntity.createGrammar()
Vector2DEntity.grammar
)
}
}

View 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
}
}

View File

@@ -19,7 +19,7 @@ export default class SimpleSerializationRotatorEntity extends RotatorEntity {
P: Number(p),
Y: Number(y),
})),
RotatorEntity.createGrammar()
RotatorEntity.grammar
)
}
}

View File

@@ -16,7 +16,7 @@ export default class SimpleSerializationVector2DEntity extends Vector2DEntity {
X: Number(x),
Y: Number(y),
})),
Vector2DEntity.createGrammar()
Vector2DEntity.grammar
)
}
}

View 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
)
}
}

View File

@@ -20,7 +20,7 @@ export default class SimpleSerializationVectorEntity extends VectorEntity {
Y: Number(y),
Z: Number(z),
})),
VectorEntity.createGrammar()
VectorEntity.grammar
)
}
}

View 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]
}
}