unreal-engine-mcp-server 0.6.0-beta-c → 0.6.0-beta-e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/CHANGELOG.md +148 -9
  2. package/README.md +2 -2
  3. package/dist/gateway/gateway-manifest.generated.json +838 -133
  4. package/dist/proxy/native-proxy.js +6 -5
  5. package/dist/server/gateway/gateway-execute-dispatch.js +2 -1
  6. package/dist/server/gateway/gateway-execute-envelope.js +5 -1
  7. package/dist/server/gateway/gateway-execute-lookup.js +1 -1
  8. package/dist/server/gateway/gateway-execute.js +17 -4
  9. package/dist/server/gateway/gateway-schema-validate.js +13 -2
  10. package/dist/server/gateway/gateway-task-results.d.ts +6 -0
  11. package/dist/server/gateway/gateway-task-results.js +40 -0
  12. package/dist/server/tool-registry-manage-tools.js +4 -1
  13. package/dist/tools/catalog/capabilities/generated/canonical-registry.generated.d.ts +2 -2
  14. package/dist/tools/catalog/capabilities/generated/canonical-registry.generated.js +2 -2
  15. package/dist/tools/catalog/capabilities/generated/canonical-registry.generated.json +4079 -618
  16. package/dist/tools/catalog/capabilities/generated/capability-cost-index.generated.d.ts +1 -1
  17. package/dist/tools/catalog/capabilities/generated/capability-cost-index.generated.js +18 -1
  18. package/dist/tools/catalog/capabilities/generated/parent-tool-definitions.generated.js +1546 -212
  19. package/dist/tools/catalog/capabilities/records/aggregate.d.ts +1 -1
  20. package/dist/tools/catalog/capabilities/records/aggregate.js +1 -1
  21. package/dist/tools/catalog/capabilities/records/build-environment/spline.data.js +4 -3
  22. package/dist/tools/catalog/capabilities/records/build-environment/water.data.js +8 -5
  23. package/dist/tools/catalog/capabilities/records/control-actor/advanced-records.js +29 -8
  24. package/dist/tools/catalog/capabilities/records/control-actor/search-records.js +2 -2
  25. package/dist/tools/catalog/capabilities/records/control-actor/spawn-records.js +13 -5
  26. package/dist/tools/catalog/capabilities/records/control-actor/state-records.js +2 -1
  27. package/dist/tools/catalog/capabilities/records/control-actor/transform-records.js +27 -9
  28. package/dist/tools/catalog/capabilities/records/control-editor/asset.js +4 -1
  29. package/dist/tools/catalog/capabilities/records/control-editor/camera.js +5 -1
  30. package/dist/tools/catalog/capabilities/records/control-editor/command.js +5 -1
  31. package/dist/tools/catalog/capabilities/records/control-editor/input.js +2 -1
  32. package/dist/tools/catalog/capabilities/records/control-editor/screenshot.js +2 -1
  33. package/dist/tools/catalog/capabilities/records/control-editor/session.js +8 -1
  34. package/dist/tools/catalog/capabilities/records/control-editor/timing.js +5 -3
  35. package/dist/tools/catalog/capabilities/records/core/builder.d.ts +1 -0
  36. package/dist/tools/catalog/capabilities/records/core/builder.js +1 -0
  37. package/dist/tools/catalog/capabilities/records/folds/build-environment.folds.js +2 -2
  38. package/dist/tools/catalog/capabilities/records/folds/control-actor.folds.js +2 -2
  39. package/dist/tools/catalog/capabilities/records/folds/manage-asset.folds.js +8 -8
  40. package/dist/tools/catalog/capabilities/records/folds/manage-audio.folds.js +5 -5
  41. package/dist/tools/catalog/capabilities/records/folds/manage-blueprint.folds.js +2 -2
  42. package/dist/tools/catalog/capabilities/records/folds/manage-effect.folds.js +3 -3
  43. package/dist/tools/catalog/capabilities/records/folds/manage-sequence.folds.js +6 -6
  44. package/dist/tools/catalog/capabilities/records/folds/system-control.folds.js +2 -2
  45. package/dist/tools/catalog/capabilities/records/gameplay/animation-physics/animation-properties.js +7 -5
  46. package/dist/tools/catalog/capabilities/records/gameplay/animation-physics/authoring-1.data.js +86 -1
  47. package/dist/tools/catalog/capabilities/records/gameplay/animation-physics/authoring-2.data.js +7 -2
  48. package/dist/tools/catalog/capabilities/records/gameplay/manage-effect/effect-properties.js +7 -6
  49. package/dist/tools/catalog/capabilities/records/gameplay/manage-effect/effect.data.js +38 -27
  50. package/dist/tools/catalog/capabilities/records/inspect/component-actor.data.js +1 -1
  51. package/dist/tools/catalog/capabilities/records/inspect/object-property.data.js +188 -10
  52. package/dist/tools/catalog/capabilities/records/inspect/properties.d.ts +1 -0
  53. package/dist/tools/catalog/capabilities/records/inspect/properties.js +2 -1
  54. package/dist/tools/catalog/capabilities/records/inspect/runtime.data.js +2 -1
  55. package/dist/tools/catalog/capabilities/records/inspect/stats.data.js +6 -2
  56. package/dist/tools/catalog/capabilities/records/manage-asset/asset-advanced.js +46 -0
  57. package/dist/tools/catalog/capabilities/records/manage-asset/asset-lifecycle.js +2 -2
  58. package/dist/tools/catalog/capabilities/records/manage-asset/asset-query.js +4 -3
  59. package/dist/tools/catalog/capabilities/records/manage-asset/builder.d.ts +3 -0
  60. package/dist/tools/catalog/capabilities/records/manage-asset/builder.js +3 -0
  61. package/dist/tools/catalog/capabilities/records/manage-asset/content-sources.js +3 -1
  62. package/dist/tools/catalog/capabilities/records/manage-asset/material-create.js +17 -2
  63. package/dist/tools/catalog/capabilities/records/manage-asset/material-graph.js +7 -5
  64. package/dist/tools/catalog/capabilities/records/manage-asset/material-params.js +5 -5
  65. package/dist/tools/catalog/capabilities/records/manage-audio/authoring.data.js +15 -5
  66. package/dist/tools/catalog/capabilities/records/manage-audio/runtime.data.js +1 -0
  67. package/dist/tools/catalog/capabilities/records/manage-blueprint/functions-events.js +3 -2
  68. package/dist/tools/catalog/capabilities/records/manage-blueprint/graph-batch.js +2 -2
  69. package/dist/tools/catalog/capabilities/records/manage-blueprint/graph-pins.js +6 -2
  70. package/dist/tools/catalog/capabilities/records/manage-blueprint/properties.js +4 -2
  71. package/dist/tools/catalog/capabilities/records/manage-blueprint/scs-components.js +7 -2
  72. package/dist/tools/catalog/capabilities/records/manage-blueprint/variables-metadata.js +8 -2
  73. package/dist/tools/catalog/capabilities/records/manage-blueprint/widget-animation.js +14 -4
  74. package/dist/tools/catalog/capabilities/records/manage-blueprint/widget-lifecycle.js +13 -5
  75. package/dist/tools/catalog/capabilities/records/manage-blueprint/widget-panels.js +1 -1
  76. package/dist/tools/catalog/capabilities/records/manage-sequence/cinematic.js +12 -3
  77. package/dist/tools/catalog/capabilities/records/manage-sequence/helpers.js +2 -2
  78. package/dist/tools/catalog/capabilities/records/manage-sequence/mrq.js +23 -4
  79. package/dist/tools/catalog/capabilities/records/manage-sequence/timeline-bindings.js +2 -2
  80. package/dist/tools/catalog/capabilities/records/manage-sequence/timeline-lifecycle.js +7 -2
  81. package/dist/tools/catalog/capabilities/records/manage-sequence/timeline-playback.js +9 -5
  82. package/dist/tools/catalog/capabilities/records/manage-sequence/timeline-state-ranges.js +19 -0
  83. package/dist/tools/catalog/capabilities/records/manage-sequence/timeline-tracks.js +10 -5
  84. package/dist/tools/catalog/capabilities/records/manage-tools/index.js +30 -2
  85. package/dist/tools/catalog/capabilities/records/system-control/console.js +3 -1
  86. package/dist/tools/catalog/capabilities/records/system-control/system-ops.js +122 -10
  87. package/dist/tools/catalog/capabilities/records/utility/output-glossary.js +11 -0
  88. package/dist/tools/catalog/capabilities/records/utility/utility-schema-pins.js +6 -3
  89. package/dist/tools/catalog/capabilities/records/world/manage-geometry.optimize.data.js +29 -0
  90. package/dist/tools/catalog/capabilities/records/world/manage-geometry.primitives.data.js +2 -1
  91. package/dist/tools/catalog/capabilities/records/world/properties.js +2 -2
  92. package/dist/tools/catalog/capabilities/semantic/envelope.d.ts +1 -1
  93. package/dist/tools/catalog/capabilities/semantic/errors.d.ts +1 -1
  94. package/dist/tools/catalog/unreal-gateway-definition.js +2 -2
  95. package/dist/utils/responses/gateway-reply-compaction.d.ts +6 -0
  96. package/dist/utils/responses/gateway-reply-compaction.js +264 -0
  97. package/dist/utils/responses/response-content.d.ts +0 -1
  98. package/dist/utils/responses/response-content.js +0 -172
  99. package/dist/utils/responses/response-validator.d.ts +1 -1
  100. package/dist/utils/responses/response-validator.js +39 -8
  101. package/package.json +2 -2
  102. package/server.json +2 -2
@@ -1,4 +1,5 @@
1
1
  import { buildRecord } from '../helpers.js';
2
+ import { ANY_EDITOR_STATE } from '../../core/builder.js';
2
3
  import { P } from '../properties.js';
3
4
  import { E } from './effect-properties.js';
4
5
  import { bool } from '../../shared/schema-props.js';
@@ -14,7 +15,7 @@ const SYSTEM_ONE_OF = ['systemPath', 'assetPath'];
14
15
  const PARAMETER_LIST = {
15
16
  type: 'array', minItems: 1, maxItems: 200,
16
17
  items: { type: 'object', properties: { parameterName: E.parameterName, parameterValue: E.parameterValue }, required: ['parameterName', 'parameterValue'], additionalProperties: false },
17
- description: 'Several values written together and saved once, in place of parameterName and parameterValue: each {parameterName, parameterValue} names a user parameter or a module input as get_niagara_info lists it (InitializeParticle.Lifetime), and emitterName and save apply to every entry. The reply lists every entry (parameterName, applied, error); the call fails PARAMETER_BATCH_INCOMPLETE naming any that did not apply, and the others stay written.',
18
+ description: 'Several values written together and saved once, in place of parameterName and parameterValue: each {parameterName, parameterValue} names a user parameter, a module input or a module\'s static switch as get_niagara_info lists it (InitializeParticle.Lifetime, InitializeParticle.Ribbon Width Mode), and emitterName and save apply to every entry. The reply lists every entry (parameterName, applied, error); the call fails PARAMETER_BATCH_INCOMPLETE naming any that did not apply, and the others stay written.',
18
19
  };
19
20
  export const EFFECT_RECORDS = [
20
21
  buildRecord({ parentTool: T, id: `${T}.particle`, action: 'particle', family: F,
@@ -23,10 +24,10 @@ export const EFFECT_RECORDS = [
23
24
  effect: 'write', latency: 'interactive', resources: 'medium', plugins: NIAGARA,
24
25
  exampleInput: { action: 'particle', preset: 'Default', location: [0, 0, 100] } }),
25
26
  buildRecord({ parentTool: T, id: `${T}.niagara`, action: 'niagara', family: F,
26
- summary: 'Author a Niagara system.', whenToUse: W, whenNotToUse: ['Use particle.'],
27
- inputProps: { name: P.name, actorName: P.actorName, systemPath: E.systemPath, attachToActor: E.attachToActor, location: P.location, rotation: P.rotation, scale: E.scale }, required: ['systemPath'],
27
+ summary: 'Place a Niagara system in the level as a Niagara actor, at a location or attached to an actor; autoActivate false places it switched off, for a sequence particle track or gameplay to start.', whenToUse: W, whenNotToUse: ['Use particle.'],
28
+ inputProps: { name: P.name, actorName: P.actorName, systemPath: E.systemPath, attachToActor: E.attachToActor, location: P.location, rotation: P.rotation, scale: E.scale, autoActivate: { type: 'boolean', description: 'false places the effect switched off so it waits for its cue (a particle track trigger key, Activate); default true, which also fires it when the level starts.' } }, required: ['systemPath'],
28
29
  effect: 'write', latency: 'interactive', resources: 'medium', plugins: NIAGARA,
29
- outputProps: { actorPath: P.string_, actorName: P.actorName, actorGuid: P.string_, existsAfter: P.bool_, actorClass: P.string_ }, outputRequired: [],
30
+ outputProps: { actorPath: P.string_, actorName: P.actorName, existsAfter: P.bool_, actorClass: P.string_ }, outputRequired: [],
30
31
  exampleInput: { action: 'niagara', systemPath: '/Game/NS_Explosion', actorName: 'FX_1' }, exampleOutput: { success: true, message: 'Niagara system authored', actorName: 'FX_1' } }),
31
32
  buildRecord({ parentTool: T, id: `${T}.debug_shape`, action: 'debug_shape', topics: ['draw debug shape', 'debug sphere box line arrow capsule', 'show debug shape', 'draw a debug line', 'draw debug line'], family: F,
32
33
  summary: 'Spawn a debug shape in the editor viewport.', whenToUse: ['Visualize a region/volume.'], whenNotToUse: ['Use spawn_niagara.'],
@@ -66,56 +67,59 @@ export const EFFECT_RECORDS = [
66
67
  effect: 'write', latency: 'interactive', resources: 'medium', plugins: NIAGARA,
67
68
  exampleInput: { action: 'create_volumetric_fog', name: 'VF_Mist', density: 0.02 } }),
68
69
  buildRecord({ parentTool: T, id: `${T}.create_particle_trail`, action: 'create_particle_trail', family: F,
69
- summary: 'Create a particle trail effect.', whenToUse: ['A trailing effect is needed.'], whenNotToUse: ['Use create_niagara_ribbon for a ribbon-based trail.'],
70
+ summary: 'Create a particle trail effect: by default a ribbon (the engine LocationBasedRibbon template given its own SpawnRate of 60 a second, Ribbon Width Mode Direct Set and an infinite loop) that trails whatever it is attached to, placed in the level only when location is given; tune SpawnRate.SpawnRate, InitializeParticle.Lifetime, Ribbon Width and Color with set_parameter_value.', whenToUse: ['A trailing effect is needed.'], whenNotToUse: ['Use create_niagara_ribbon for a ribbon-based trail.'],
70
71
  inputProps: { name: P.name, path: P.path, systemPath: E.systemPath, templateEmitterPath: E.templateEmitterPath, location: P.location, save: SAVE }, required: ['name'],
71
72
  effect: 'write', latency: 'interactive', resources: 'medium', plugins: NIAGARA,
72
73
  exampleInput: { action: 'create_particle_trail', name: 'PT_Trail' } }),
73
74
  buildRecord({ parentTool: T, id: `${T}.create_environment_effect`, action: 'create_environment_effect', family: F,
74
- summary: 'Create an environment effect (weather/atmosphere).', whenToUse: ['Ambient FX needed.'], whenNotToUse: ['Use build_environment weather.'],
75
+ summary: 'Create an environment effect (weather/atmosphere) system asset, placed in the level only when location is given.', whenToUse: ['Ambient FX needed.'], whenNotToUse: ['Use build_environment weather.'],
75
76
  inputProps: { name: P.name, path: P.path, systemPath: E.systemPath, templateEmitterPath: E.templateEmitterPath, location: P.location, save: SAVE }, required: ['name'],
76
77
  effect: 'write', latency: 'interactive', resources: 'medium', plugins: NIAGARA,
77
78
  exampleInput: { action: 'create_environment_effect', name: 'EE_Rain' } }),
78
79
  buildRecord({ parentTool: T, id: `${T}.create_impact_effect`, action: 'create_impact_effect', family: F,
79
- summary: 'Create an impact effect.', whenToUse: ['Hit FX needed.'], whenNotToUse: ['Use create_niagara_system.'],
80
+ summary: 'Create an impact effect system asset (the engine OmnidirectionalBurst template by default), placed in the level only when location is given.', whenToUse: ['Hit FX needed.'], whenNotToUse: ['Use create_niagara_system.'],
80
81
  inputProps: { name: P.name, path: P.path, systemPath: E.systemPath, templateEmitterPath: E.templateEmitterPath, location: P.location, save: SAVE }, required: ['name'],
81
82
  effect: 'write', latency: 'interactive', resources: 'medium', plugins: NIAGARA,
82
83
  exampleInput: { action: 'create_impact_effect', name: 'IE_Bullet' } }),
83
84
  buildRecord({ parentTool: T, id: `${T}.create_niagara_ribbon`, action: 'create_niagara_ribbon', family: F,
84
- summary: 'Create a Niagara ribbon effect.', whenToUse: ['Trail/ribbon FX needed.'], whenNotToUse: ['Use create_niagara_system.'],
85
+ summary: 'Create a Niagara ribbon effect: by default the engine LocationBasedRibbon template given its own SpawnRate of 60 a second, Ribbon Width Mode Direct Set and an infinite loop, so it trails whatever it is attached to, placed in the level only when location is given; tune SpawnRate.SpawnRate, InitializeParticle.Lifetime, Ribbon Width and Color with set_parameter_value.', whenToUse: ['Trail/ribbon FX needed.'], whenNotToUse: ['Use create_niagara_system.'],
85
86
  inputProps: { name: P.name, path: P.path, systemPath: E.systemPath, templateEmitterPath: E.templateEmitterPath, location: P.location, save: SAVE }, required: ['name'],
86
87
  effect: 'write', latency: 'interactive', resources: 'medium', plugins: NIAGARA,
87
88
  exampleInput: { action: 'create_niagara_ribbon', name: 'NR_Slash' } }),
88
89
  buildRecord({ parentTool: T, id: `${T}.activate`, action: 'activate', family: F,
89
- summary: 'Activate a spawned effect component (PIE).', whenToUse: ['Effect must turn on at runtime.'], whenNotToUse: ['Author the effect.'],
90
+ summary: 'Activate a placed Niagara component, in the level editor or a running game; reset restarts it from age 0.', whenToUse: ['Effect must turn on.'], whenNotToUse: ['Author the effect.'],
90
91
  inputProps: { actorName: P.actorName, reset: E.reset }, required: ['actorName'],
91
- effect: 'write', editorStates: ['pie', 'simulate'], behavior: { idempotency: 'idempotent' }, latency: 'instant', resources: 'low', plugins: NIAGARA,
92
+ effect: 'write', editorStates: ANY_EDITOR_STATE, behavior: { idempotency: 'idempotent' }, latency: 'instant', resources: 'low', plugins: NIAGARA,
92
93
  exampleInput: { action: 'activate', actorName: 'FX_1', reset: false } }),
93
94
  buildRecord({ parentTool: T, id: `${T}.activate_effect`, action: 'activate_effect', family: F,
94
95
  summary: 'Activate a Niagara component by actor, or by the system asset it plays (on actorName, else anywhere in the world).', whenToUse: ['Asset-driven runtime FX.'], whenNotToUse: ['Use activate.'],
95
96
  inputProps: { actorName: P.actorName, assetPath: { type: 'string', description: 'Niagara system asset the component plays; picks that component on actorName, or the first one in the world without actorName.' }, systemName: E.systemName, reset: E.reset }, required: [], requiredOneOf: ['actorName', 'assetPath', 'systemName'],
96
- effect: 'write', editorStates: ['pie', 'simulate'], behavior: {}, latency: 'instant', resources: 'low', plugins: NIAGARA,
97
+ effect: 'write', editorStates: ANY_EDITOR_STATE, behavior: {}, latency: 'instant', resources: 'low', plugins: NIAGARA,
97
98
  exampleInput: { action: 'activate_effect', systemName: 'FX_1' } }),
98
99
  buildRecord({ parentTool: T, id: `${T}.deactivate`, action: 'deactivate', family: F,
99
- summary: 'Deactivate a runtime effect component.', whenToUse: ['Effect must turn off.'], whenNotToUse: ['Use activate.'],
100
+ summary: 'Deactivate a placed Niagara component, in the level editor or a running game.', whenToUse: ['Effect must turn off.'], whenNotToUse: ['Use activate.'],
100
101
  inputProps: { actorName: P.actorName }, required: ['actorName'],
101
- effect: 'write', editorStates: ['pie', 'simulate'], behavior: { idempotency: 'idempotent' }, latency: 'instant', resources: 'low', plugins: NIAGARA,
102
+ effect: 'write', editorStates: ANY_EDITOR_STATE, behavior: { idempotency: 'idempotent' }, latency: 'instant', resources: 'low', plugins: NIAGARA,
102
103
  exampleInput: { action: 'deactivate', actorName: 'FX_1' } }),
103
104
  buildRecord({ parentTool: T, id: `${T}.reset`, action: 'reset', family: F,
104
- summary: 'Reset a runtime effect to initial state.', whenToUse: ['Effect must restart.'], whenNotToUse: ['Use deactivate.'],
105
+ summary: 'Restart a placed Niagara component from age 0, in the level editor or a running game.', whenToUse: ['Effect must restart.'], whenNotToUse: ['Use deactivate.'],
105
106
  inputProps: { actorName: P.actorName }, required: ['actorName'],
106
- effect: 'write', editorStates: ['pie', 'simulate'], behavior: { safeToRetry: true }, latency: 'instant', resources: 'low', plugins: NIAGARA,
107
+ effect: 'write', editorStates: ANY_EDITOR_STATE, behavior: { safeToRetry: true }, latency: 'instant', resources: 'low', plugins: NIAGARA,
107
108
  exampleInput: { action: 'reset', actorName: 'FX_1' } }),
108
109
  buildRecord({ parentTool: T, id: `${T}.advance_simulation`, action: 'advance_simulation', family: F,
109
- summary: 'Advance a Niagara simulation by time (PIE/editor).', whenToUse: ['Step the sim manually.'], whenNotToUse: ['Let it run.'],
110
- inputProps: { actorName: P.actorName, deltaTime: E.deltaTime, steps: E.steps }, required: [],
110
+ topics: ['preview effect', 'effect at an age', 'scrub particles', 'step niagara'],
111
+ summary: 'Advance a placed Niagara system by steps of deltaTime right away, in the level editor as in a running game, to look at it at a chosen age: spawn it with spawn_niagara, advance it, then capture it with control_editor screenshot from a camera location. The reply gives the age reached and whether the system has finished; NOT_ADVANCED means it did not move (finished, paused or not running).',
112
+ whenToUse: ['An effect must be judged at chosen ages (a splash 0.2 s and 0.6 s after it starts) without starting Play In Editor: advance with reset true to the first age, screenshot, advance again; the ages add up.'],
113
+ whenNotToUse: ['The effect should simply play in a running game (it runs by itself).'],
114
+ inputProps: { actorName: P.actorName, deltaTime: E.deltaTime, steps: E.steps, reset: bool('Restart the system from age 0 before stepping, so it ends at exactly steps x deltaTime seconds; also plays a finished system again. Without it the steps add to the age it has.') }, required: ['actorName'],
111
115
  effect: 'write', editorStates: ['edit', 'pie', 'simulate'], latency: 'instant', resources: 'low', plugins: NIAGARA,
112
- exampleInput: { action: 'advance_simulation', deltaTime: 0.016, steps: 2 } }),
116
+ exampleInput: { action: 'advance_simulation', actorName: 'FX_Splash', reset: true, deltaTime: 0.02, steps: 10 } }),
113
117
  buildRecord({ parentTool: T, id: `${T}.add_niagara_module`, action: 'add_niagara_module', family: F,
114
118
  summary: 'Add a module to a Niagara emitter.', whenToUse: ['Emitter needs a module.'], whenNotToUse: ['Use connect_niagara_pins.'],
115
119
  inputProps: { assetPath: P.assetPath, systemPath: E.systemPath, emitterName: E.emitterName, modulePath: E.modulePath, scriptType: E.scriptType, save: SAVE }, required: [], requiredOneOf: SYSTEM_ONE_OF,
116
120
  effect: 'write', latency: 'interactive', resources: 'low', plugins: NIAGARA,
117
121
  outputProps: { nodeId: E.nodeId }, outputRequired: [],
118
- exampleInput: { action: 'add_niagara_module', assetPath: '/Game/NS_Fire', emitterName: 'Spark', modulePath: '/Niagara/Modules/Emitter/EmitterState.EmitterState', scriptType: 'Update' }, exampleOutput: { success: true, message: 'Niagara module added', nodeId: 'A1B2C3' } }),
122
+ exampleInput: { action: 'add_niagara_module', assetPath: '/Game/NS_Fire', emitterName: 'Spark', modulePath: '/Niagara/Modules/Update/Color/ScaleColor.ScaleColor', scriptType: 'Update' }, exampleOutput: { success: true, message: 'Niagara module added', nodeId: 'A1B2C3' } }),
119
123
  buildRecord({ parentTool: T, id: `${T}.connect_niagara_pins`, action: 'connect_niagara_pins', family: F,
120
124
  summary: 'Connect two Niagara graph pins (fromNode, fromPin, toNode, toPin), or with autoConnect wire the first compatible free pins.', whenToUse: ['Modules must wire together.'], whenNotToUse: ['Use add_niagara_module.'],
121
125
  inputProps: { assetPath: P.assetPath, systemPath: E.systemPath, emitterName: E.emitterName, scriptType: E.scriptType, fromNode: E.fromNode, fromPin: E.fromPin, toNode: E.toNode, toPin: E.toPin, autoConnect: E.autoConnect, save: SAVE }, required: [], requiredOneOf: SYSTEM_ONE_OF,
@@ -215,24 +219,31 @@ export const EFFECT_RECORDS = [
215
219
  effect: 'write', latency: 'interactive', resources: 'low', plugins: NIAGARA,
216
220
  exampleInput: { action: 'add_user_parameter', systemPath: '/Game/NS_Fire', parameterName: 'Intensity', parameterType: 'Float' } }),
217
221
  buildRecord({ parentTool: T, id: `${T}.set_parameter_value`, action: 'set_parameter_value', family: F,
218
- summary: 'Set Niagara parameter values: a user parameter, or a module input named as get_niagara_info lists it (InitializeParticle.Lifetime); one with parameterName and parameterValue, or several with parameters, written together and saved once.',
219
- whenToUse: ['A user parameter or a module input value must change.', 'Several values of one system or emitter must change together (parameters): one call, one save.'],
222
+ summary: 'Set Niagara parameter values: a user parameter, a module input named as get_niagara_info lists it (InitializeParticle.Lifetime), or a module\'s static switch (InitializeParticle.Ribbon Width Mode, an enum by its display name such as Direct Set); one with parameterName and parameterValue, or several with parameters, written together and saved once.',
223
+ whenToUse: ['A user parameter or a module input value must change.', 'Several values of one system or emitter must change together (parameters): one call, one save.',
224
+ 'A module input changes nothing because a static switch gates it (a Ribbon Width written while InitializeParticle.Ribbon Width Mode is Unset): set the switch; get_niagara_info lists every staticSwitches value.'],
220
225
  whenNotToUse: ['The user parameter does not exist yet (use add_user_parameter).'],
221
- inputProps: { systemPath: E.systemPath, assetPath: P.assetPath, emitterName: { type: 'string', description: 'Limit a module-input match to this emitter (user parameters are system-wide).' }, parameterName: E.parameterName, parameterValue: E.parameterValue, parameters: PARAMETER_LIST, save: SAVE }, required: [], requiredOneOf: ['parameterName', 'parameters'],
226
+ inputProps: { systemPath: E.systemPath, assetPath: P.assetPath, emitterName: { type: 'string', description: 'Limit a module-input or static-switch match to this emitter (user parameters are system-wide).' }, parameterName: E.parameterName, parameterValue: E.parameterValue, parameters: PARAMETER_LIST, save: SAVE }, required: [], requiredOneOf: ['parameterName', 'parameters'],
222
227
  effect: 'write', behavior: { idempotency: 'idempotent' }, latency: 'interactive', resources: 'low', plugins: NIAGARA,
223
228
  outputProps: {
224
229
  parameterName: E.parameterName,
225
- moduleInputCopiesWritten: { type: 'number', description: 'Module input: how many copies of it (one per script that runs the module) took the value.' },
230
+ moduleInputCopiesWritten: { type: 'number', description: 'Module input: how many copies of it (one per script that runs the module) took the value; static switch: how many module nodes took it.' },
226
231
  saved: bool('Whether the system was saved afterwards.'),
227
232
  parameters: { type: 'array', items: { type: 'object', 'x-unreal-reflection-boundary': true }, description: 'A parameters call: each entry {parameterName, applied, error (when it did not apply), moduleInputCopiesWritten}.' },
228
233
  applied: { type: 'number', description: 'A parameters call: how many entries were written.' },
229
234
  }, outputRequired: [],
230
235
  exampleInput: { action: 'set_parameter_value', systemPath: '/Game/NS_Fire', parameterName: 'Intensity', parameterValue: 2 } }),
231
236
  buildRecord({ parentTool: T, id: `${T}.bind_parameter_to_source`, action: 'bind_parameter_to_source', family: F,
232
- summary: 'Bind a parameter to a data source.', whenToUse: ['Param driven externally.'], whenNotToUse: ['Use set_parameter_value.'],
233
- inputProps: { systemPath: E.systemPath, assetPath: P.assetPath, emitterName: E.emitterName, parameterName: E.parameterName, parameterType: E.parameterType, sourceBinding: E.sourceBinding, save: SAVE }, required: ['parameterName'], requiredOneOf: SYSTEM_ONE_OF,
237
+ summary: 'Drive a Niagara value from a parameter: a module input named as get_niagara_info lists it (InitializeParticle.Color) is linked to sourceBinding, as the stack\'s Link Inputs menu does; any other parameterName gets a Set Variables module in particle update that writes it from sourceBinding every frame.',
238
+ whenToUse: ['A Blueprint sets a value at run time (Set Niagara Variable on a user parameter): parameterName is the module input as get_niagara_info lists it (InitializeParticle.Color), sourceBinding the user parameter (User.Tint).', 'A value must follow an engine, system or emitter parameter (sourceBinding Emitter.Age).'],
239
+ whenNotToUse: ['A fixed value is enough (use set_parameter_value).', 'The user parameter does not exist yet (use add_user_parameter first).'],
240
+ inputProps: { systemPath: E.systemPath, assetPath: P.assetPath, emitterName: E.emitterName,
241
+ parameterName: { type: 'string', description: 'The module input to link (InitializeParticle.Color, as get_niagara_info lists moduleInputs), or a variable for a Set Variables module to write (Particles.Glow).' },
242
+ parameterType: { type: 'string', description: 'Set Variables only: the type of the variable it writes (Float, Vector, LinearColor ...); a module input keeps its own type.' },
243
+ sourceBinding: { type: 'string', description: 'The parameter to read: a user parameter of the same type (User.Tint) or an engine, system or emitter one (Emitter.Age).' },
244
+ save: SAVE }, required: ['parameterName', 'sourceBinding'], requiredOneOf: SYSTEM_ONE_OF,
234
245
  effect: 'write', latency: 'interactive', resources: 'low', plugins: NIAGARA,
235
- exampleInput: { action: 'bind_parameter_to_source', systemPath: '/Game/NS_Fire', parameterName: 'Intensity', sourceBinding: 'Emitter.Age' } }),
246
+ exampleInput: { action: 'bind_parameter_to_source', systemPath: '/Game/NS_Fire', parameterName: 'InitializeParticle.Color', sourceBinding: 'User.Tint' } }),
236
247
  buildRecord({ parentTool: T, id: `${T}.set_niagara_dynamic_input`, action: 'set_niagara_dynamic_input', family: F,
237
248
  summary: 'Configure a Niagara dynamic input expression.', whenToUse: ['Expression-driven value needed.'], whenNotToUse: ['Use set_parameter_value.'],
238
249
  inputProps: { systemPath: E.systemPath, assetPath: P.assetPath, emitterName: E.emitterName, targetNodeId: E.targetNodeId, moduleName: { type: 'string', description: 'Module (function call) name that owns inputName; alternative to targetNodeId.' }, inputName: E.inputName, dynamicInputScriptPath: E.dynamicInputScriptPath, replaceExisting: E.replaceExisting, save: SAVE }, required: ['inputName'], requiredOneOf: SYSTEM_ONE_OF,
@@ -245,7 +256,7 @@ export const EFFECT_RECORDS = [
245
256
  exampleInput: { action: 'add_sprite_renderer_module', systemPath: '/Game/NS_Fire', emitterName: 'Spark', materialPath: '/Engine/BasicShapes/BasicShapeMaterial.BasicShapeMaterial' } }),
246
257
  buildRecord({ parentTool: T, id: `${T}.add_mesh_renderer_module`, action: 'add_mesh_renderer_module', family: F,
247
258
  summary: 'Add a mesh renderer module.', whenToUse: ['Mesh rendering needed.'], whenNotToUse: ['Use add_sprite_renderer_module.'],
248
- inputProps: { systemPath: E.systemPath, assetPath: P.assetPath, emitterName: E.emitterName, meshPath: P.meshPath, save: SAVE }, required: EMITTER_ONLY, requiredOneOf: SYSTEM_ONE_OF,
259
+ inputProps: { systemPath: E.systemPath, assetPath: P.assetPath, emitterName: E.emitterName, meshPath: E.meshPath, save: SAVE }, required: EMITTER_ONLY, requiredOneOf: SYSTEM_ONE_OF,
249
260
  effect: 'write', latency: 'interactive', resources: 'low', plugins: NIAGARA,
250
261
  exampleInput: { action: 'add_mesh_renderer_module', systemPath: '/Game/NS_Debris', emitterName: 'Chunks', meshPath: '/Engine/BasicShapes/Cube.Cube' } }),
251
262
  buildRecord({ parentTool: T, id: `${T}.add_ribbon_renderer_module`, action: 'add_ribbon_renderer_module', family: F,
@@ -33,7 +33,7 @@ export const COMPONENT_ACTOR_RECORDS = [
33
33
  effect: 'read',
34
34
  exampleInput: { action: 'get_component_property', actorName: 'PlayerStart_1', componentName: 'Billboard', propertyName: 'Sprite' },
35
35
  exampleOutput: { success: true, message: 'Component property read', value: null },
36
- outputProps: { value: P.value },
36
+ outputProps: { value: P.valueRead },
37
37
  }),
38
38
  buildCoreRecord({
39
39
  parentTool: 'inspect', action: 'set_component_property', dispatchAction: 'control_actor', domain: D, family: 'components',
@@ -1,4 +1,4 @@
1
- import { buildCoreRecord } from '../core/builder.js';
1
+ import { ANY_EDITOR_STATE, buildCoreRecord } from '../core/builder.js';
2
2
  import { P } from './properties.js';
3
3
  const D = 'inspect';
4
4
  const INSPECT_OBJECT_OUTPUT = {
@@ -132,6 +132,171 @@ export const OBJECT_PROPERTY_RECORDS = [
132
132
  },
133
133
  },
134
134
  }),
135
+ buildCoreRecord({
136
+ parentTool: 'inspect', action: 'check_mesh', dispatchAction: 'check_mesh', domain: D, family: 'object',
137
+ editorStates: ANY_EDITOR_STATE,
138
+ summary: 'Check whether a static mesh\'s geometry is sound, from its source triangles: closed (watertight) or open with how many holes, edges shared by three or more faces, faces turned inside out, faces with no area, seams where faces do not share their corners, and how many separate parts it is made of, with its size, surface area and (when closed) volume.',
139
+ topics: ['check mesh', 'mesh holes', 'mesh has holes', 'watertight', 'non manifold edges', 'flipped normals', 'inside out faces', 'mesh parts', 'broken mesh', 'mesh volume'],
140
+ whenToUse: ['An imported, downloaded or generated mesh must be proven sound before it is placed, merged, used for collision or booleans, or shipped.', 'A mesh shows see-through gaps, black or inside-out faces, or splits into loose pieces.'],
141
+ whenNotToUse: ['Only the mesh\'s size, pivot, slots or triangle counts per material are needed (use inspect_object objectKind mesh).', 'The mesh must be cut into one asset per part (use manage_geometry split_mesh).'],
142
+ inputProps: {
143
+ meshPath: { type: 'string', description: 'Static mesh asset path, e.g. /Game/Meshes/SM_Rock. Its source triangles (LOD 0 as imported or built) are checked, so a Nanite mesh answers at full detail. A placed actor\'s mesh is named by get_components.' },
144
+ },
145
+ required: ['meshPath'],
146
+ effect: 'read',
147
+ costLatency: 'interactive',
148
+ exampleInput: { action: 'check_mesh', meshPath: '/Engine/BasicShapes/Cube' },
149
+ exampleOutput: {
150
+ success: true, message: 'Cube: 48 triangles in 1 part(s), closed.', meshPath: '/Engine/BasicShapes/Cube.Cube',
151
+ triangles: 48, vertices: 26, size: [100, 100, 100], surfaceArea: 60000, closed: true, volume: 1000000,
152
+ openEdges: 0, holes: 0, seamEdges: 0, nonManifoldEdges: 0, flippedEdges: 0, degenerateTriangles: 0, parts: 1,
153
+ largestParts: [{ triangles: 48, size: [100, 100, 100], center: [0, 0, 0] }],
154
+ },
155
+ outputProps: {
156
+ meshPath: { type: 'string', description: 'The mesh checked.' },
157
+ triangles: { type: 'number', description: 'Source triangles (LOD 0).' },
158
+ vertices: { type: 'number', description: 'Distinct corner positions (to 0.01 cm): corners at one place count once, whether or not the source shares the vertex.' },
159
+ size: { type: 'array', items: { type: 'number' }, description: 'Extent [x, y, z] in cm, in the mesh\'s own space.' },
160
+ surfaceArea: { type: 'number', description: 'Total face area in cm².' },
161
+ closed: { type: 'boolean', description: 'True when every edge joins exactly two faces (seams count as joined): the surface is watertight (a solid, not a sheet or a shell with gaps).' },
162
+ volume: { type: 'number', description: 'With closed: the volume it encloses, in cm³.' },
163
+ openEdges: { type: 'number', description: 'Edges with a face on one side only and none on the other at that place: the outlines of holes and open rims.' },
164
+ holes: { type: 'number', description: 'Separate outlines those open edges form (one per hole or open rim).' },
165
+ seamEdges: { type: 'number', description: 'Places where two faces meet without sharing their corners (split for flat shading or UV seams): the surface renders whole, but booleans, remeshing and collision built from the mesh need it welded.' },
166
+ nonManifoldEdges: { type: 'number', description: 'Edges shared by three or more faces (fins, faces stacked on one edge); they break booleans, remeshing and collision generation.' },
167
+ flippedEdges: { type: 'number', description: 'Edges (seams included) whose two faces wind the same way along them, so one of the two faces points inward (a face turned inside out renders dark or vanishes).' },
168
+ degenerateTriangles: { type: 'number', description: 'Faces with no area (slivers or a repeated corner).' },
169
+ parts: { type: 'number', description: 'Separate pieces: groups of faces joined by corners at the same place.' },
170
+ largestParts: { type: 'array', items: { type: 'object', additionalProperties: true, 'x-unreal-reflection-boundary': true }, description: 'Up to 8 pieces, largest first: triangles, size [x, y, z] and center [x, y, z] in cm, in the mesh\'s own space.' },
171
+ },
172
+ outputRequired: ['meshPath', 'triangles', 'closed', 'parts'],
173
+ }),
174
+ buildCoreRecord({
175
+ parentTool: 'inspect', action: 'describe_view', dispatchAction: 'describe_view', domain: D, family: 'object',
176
+ editorStates: ['edit', 'simulate'],
177
+ summary: 'Describe what the level viewport\'s camera sees, as text: each actor in view with its share of the frame, its box on screen, how bright it is drawn and its distance, the share that is background (sky, void), and the frame\'s brightness. location and rotation place the camera first, as control_editor screenshot takes them.',
178
+ topics: ['what is visible', 'what the camera sees', 'describe view', 'describe frame', 'is it on screen', 'player on screen', 'screen coverage', 'check the camera shot', 'in shot', 'visible actors', 'frame brightness', 'too dark'],
179
+ whenToUse: ['A shot must be checked without looking at a picture: is the subject in view, how much of the frame it fills, where it sits, whether it is lit or lost in the dark.', 'A camera placement must be compared or tuned by numbers (subject coverage, sky share, brightness).'],
180
+ whenNotToUse: ['A picture is wanted (use control_editor screenshot).', 'Play In Editor runs: the level viewport does not show the running game (stop it, or use Simulate).', 'Whether something blocks a line of sight (use raycast_world).'],
181
+ inputProps: {
182
+ location: { type: 'object', properties: { x: { type: 'number' }, y: { type: 'number' }, z: { type: 'number' } }, additionalProperties: false, description: 'Camera location {x, y, z} in cm; omitted, the viewport\'s current one. The camera stays there.' },
183
+ rotation: { type: 'object', properties: { pitch: { type: 'number' }, yaw: { type: 'number' }, roll: { type: 'number' } }, additionalProperties: false, description: 'Camera rotation {pitch, yaw, roll} in degrees; omitted, the viewport\'s current one.' },
184
+ limit: { type: 'number', description: 'Most actors to list, largest share first (default 20, at most 100); visibleActors counts them all.' },
185
+ },
186
+ required: [],
187
+ effect: 'read',
188
+ costLatency: 'interactive',
189
+ exampleInput: { action: 'describe_view', location: { x: 0, y: -600, z: 200 }, rotation: { pitch: -10, yaw: 90, roll: 0 } },
190
+ exampleOutput: {
191
+ success: true, message: '2 actor(s) in view; the largest is Floor at 41.2% of the frame.',
192
+ cameraLocation: { x: 0, y: -600, z: 200 }, cameraRotation: { pitch: -10, yaw: 90, roll: 0 }, visibleActors: 2,
193
+ actors: [
194
+ { name: 'Floor', class: 'StaticMeshActor', coverage: 41.2, box: [0, 52.4, 100, 100], brightness: 0.34, distance: 220 },
195
+ { name: 'Crate', class: 'StaticMeshActor', coverage: 6.8, box: [44.1, 38.2, 55.9, 61.3], brightness: 0.51, distance: 560 },
196
+ ],
197
+ background: 52, other: 0, brightness: { p10: 0.05, median: 0.31, p90: 0.62, clipped: 0, dark: 4.1 },
198
+ },
199
+ outputProps: {
200
+ cameraLocation: { type: 'object', additionalProperties: true, 'x-unreal-reflection-boundary': true, description: 'Where the camera was.' },
201
+ cameraRotation: { type: 'object', additionalProperties: true, 'x-unreal-reflection-boundary': true, description: 'Where it looked.' },
202
+ visibleActors: { type: 'number', description: 'Actors the game would draw with at least one pixel in the frame.' },
203
+ actors: { type: 'array', items: { type: 'object', additionalProperties: true, 'x-unreal-reflection-boundary': true }, description: 'Largest share first: name (actor label), class, coverage (percent of the frame), box [left, top, right, bottom] in percent of the frame\'s width and height, brightness (0-1, as drawn) and distance (cm from the camera to its bounds). Editor icons and shapes hidden in game are left out.' },
204
+ background: { type: 'number', description: 'Percent of the frame where no object was drawn (sky, void).' },
205
+ other: { type: 'number', description: 'Percent drawn by what only the editor shows (icons, gizmos, shapes hidden in game) or no actor owns (BSP).' },
206
+ brightness: { type: 'object', properties: { p10: { type: 'number' }, median: { type: 'number' }, p90: { type: 'number' }, clipped: { type: 'number' }, dark: { type: 'number' } }, additionalProperties: false, description: 'The frame as drawn: p10, median and p90 brightness (0-1), and the percent of the frame clipped (a channel at full) or dark (below 0.05).' },
207
+ },
208
+ outputRequired: ['visibleActors', 'actors', 'background', 'brightness'],
209
+ }),
210
+ buildCoreRecord({
211
+ parentTool: 'inspect', action: 'capture_passes', dispatchAction: 'capture_passes', domain: D, family: 'object',
212
+ editorStates: ['edit', 'simulate'],
213
+ summary: 'Capture the level viewport\'s view as machine-readable passes, aligned pixel for pixel: id (a PNG with one colour per actor, and the legend naming each colour\'s actor), depth (an EXR of the distance in cm) and normal (a PNG of the world normal, RGB = normal x 0.5 + 0.5), with a summary of each. location and rotation place the camera first, as describe_view and control_editor screenshot take them.',
214
+ topics: ['capture passes', 'id pass', 'object id', 'depth pass', 'depth map', 'normal pass', 'normal map of the view', 'segmentation mask', 'render passes'],
215
+ whenToUse: ['A frame must be analysed by a script, a probe or a model: which actor covers a region (id), how far each pixel is (depth), which way surfaces face (normal).', 'Regions of a screenshot must be attributed to actors, or distances in a shot measured.'],
216
+ whenNotToUse: ['A picture to look at is wanted (use control_editor screenshot).', 'Only which actors are in view and their share is needed (use describe_view, which writes no files).', 'Passes of a rendered sequence are needed (use manage_sequence add_render_pass).'],
217
+ inputProps: {
218
+ location: { type: 'object', properties: { x: { type: 'number' }, y: { type: 'number' }, z: { type: 'number' } }, additionalProperties: false, description: 'Camera location {x, y, z} in cm; omitted, the viewport\'s current one. The camera stays there.' },
219
+ rotation: { type: 'object', properties: { pitch: { type: 'number' }, yaw: { type: 'number' }, roll: { type: 'number' } }, additionalProperties: false, description: 'Camera rotation {pitch, yaw, roll} in degrees; omitted, the viewport\'s current one.' },
220
+ passes: { type: 'array', items: { type: 'string', enum: ['id', 'depth', 'normal'] }, description: 'Passes to write (default all three).' },
221
+ width: { type: 'number', description: 'Width of every pass in pixels (default 960, 64 to 4096); the height follows the viewport\'s shape so the passes line up with it.' },
222
+ outputPath: { type: 'string', description: 'Folder inside the project, project-relative or absolute (default Saved/Captures).' },
223
+ name: { type: 'string', description: 'File name prefix (default Passes_<date>_<time>): files are <name>_id.png, <name>_depth.exr and <name>_normal.png.' },
224
+ },
225
+ required: [],
226
+ effect: 'read',
227
+ costLatency: 'interactive',
228
+ exampleInput: { action: 'capture_passes', location: { x: 0, y: -600, z: 200 }, rotation: { pitch: -10, yaw: 90, roll: 0 }, name: 'Crate' },
229
+ exampleOutput: {
230
+ success: true, message: 'Captured 3 pass(es) at 960x404.', cameraLocation: { x: 0, y: -600, z: 200 }, cameraRotation: { pitch: -10, yaw: 90, roll: 0 },
231
+ fov: 90, width: 960, height: 404,
232
+ files: { id: 'X:/Games/MyGame/Saved/Captures/Crate_id.png', depth: 'X:/Games/MyGame/Saved/Captures/Crate_depth.exr', normal: 'X:/Games/MyGame/Saved/Captures/Crate_normal.png' },
233
+ ids: [{ color: '#ff3333', name: 'Floor', class: 'StaticMeshActor', coverage: 41.2 }, { color: '#33ffb2', name: 'Crate', class: 'StaticMeshActor', coverage: 6.8 }],
234
+ depth: { min: 220, median: 640, max: 2400, sky: 52 }, normal: { up: 61.3, down: 0, side: 38.7 },
235
+ },
236
+ outputProps: {
237
+ cameraLocation: { type: 'object', additionalProperties: true, 'x-unreal-reflection-boundary': true, description: 'Where the camera was.' },
238
+ cameraRotation: { type: 'object', additionalProperties: true, 'x-unreal-reflection-boundary': true, description: 'Where it looked.' },
239
+ fov: { type: 'number', description: 'Horizontal field of view in degrees.' },
240
+ width: { type: 'number', description: 'Pass width in pixels.' },
241
+ height: { type: 'number', description: 'Pass height in pixels.' },
242
+ files: { type: 'object', properties: { id: { type: 'string' }, depth: { type: 'string' }, normal: { type: 'string' } }, additionalProperties: false, description: 'Absolute path of each pass written.' },
243
+ ids: { type: 'array', items: { type: 'object', additionalProperties: true, 'x-unreal-reflection-boundary': true }, description: 'id: each actor in the id image, largest first (up to 256): color (#rrggbb in the PNG), name (actor label), class and coverage (percent of the frame). Black is background (sky, void), dark grey (#404040) what only the editor draws.' },
244
+ depth: { type: 'object', properties: { min: { type: 'number' }, median: { type: 'number' }, max: { type: 'number' }, sky: { type: 'number' } }, additionalProperties: false, description: 'depth: nearest, median and farthest distance of the drawn pixels in cm, and the percent that is sky (nothing within 100 km).' },
245
+ normal: { type: 'object', properties: { up: { type: 'number' }, down: { type: 'number' }, side: { type: 'number' } }, additionalProperties: false, description: 'normal: percent of the drawn pixels facing up (normal z above 0.7), down (below -0.7) and sideways.' },
246
+ },
247
+ outputRequired: ['files'],
248
+ }),
249
+ buildCoreRecord({
250
+ parentTool: 'inspect', action: 'raycast_world', dispatchAction: 'raycast_world', domain: D, family: 'object',
251
+ editorStates: ANY_EDITOR_STATE,
252
+ summary: 'Trace lines through the level against collision and read what each one hits first: the point, the surface normal, the distance, the actor and the component. Finds the ground under a point, checks a line of sight, and proves a wall, floor or volume really blocks. Traces the running game while Play In Editor runs.',
253
+ topics: ['line trace', 'raycast world', 'what is below', 'ground height', 'line of sight', 'does it block', 'collision test', 'trace channel'],
254
+ whenToUse: [
255
+ 'The ground height under a point, or what a jump or a drop would land on, must be known.',
256
+ 'A line of sight between two points (a camera, an enemy, the player) must be checked.',
257
+ 'A placed wall, floor, platform or blocking volume must be proven to block: a mesh or volume with no collision is traced straight through.',
258
+ ],
259
+ whenNotToUse: ['The surface of a mesh asset must be found without placing it (use raycast_mesh).', 'Only an actor\'s overall size is needed (use query_object lookup bounding_box).'],
260
+ inputProps: {
261
+ rays: {
262
+ type: 'array', minItems: 1, maxItems: 256,
263
+ items: {
264
+ type: 'object', additionalProperties: false, required: ['start', 'end'],
265
+ properties: {
266
+ start: { type: 'object', additionalProperties: true, 'x-unreal-reflection-boundary': true, description: 'Line start {x, y, z} in world space, in cm.' },
267
+ end: { type: 'object', additionalProperties: true, 'x-unreal-reflection-boundary': true, description: 'Line end {x, y, z} in world space; nothing past it is reported.' },
268
+ },
269
+ },
270
+ description: 'Lines in world space; each answers in hits at the same index.',
271
+ },
272
+ channel: { type: 'string', description: 'Collision channel traced: Visibility (default), Camera, WorldStatic, WorldDynamic, Pawn, PhysicsBody, Vehicle, Destructible, or a project trace channel by its name. What blocks depends on each component\'s response to that channel: most meshes block Visibility, a character capsule blocks Pawn.' },
273
+ traceComplex: { type: 'boolean', description: 'Trace the render triangles of meshes that allow it instead of their simple collision (default false: the shapes the game\'s movement collides with).' },
274
+ ignoreActors: { type: 'array', items: { type: 'string' }, description: 'Actors (label, name or path) the lines pass through, such as the player pawn a line starts inside; an unknown name is refused.' },
275
+ },
276
+ required: ['rays'],
277
+ effect: 'read',
278
+ exampleInput: { action: 'raycast_world', rays: [{ start: { x: 0, y: 0, z: 1000 }, end: { x: 0, y: 0, z: -1000 } }] },
279
+ exampleOutput: { success: true, hitCount: 1, hits: [{ hit: true, location: { x: 0, y: 0, z: 0 }, normal: { x: 0, y: 0, z: 1 }, distance: 1000, actorName: 'Floor', componentName: 'StaticMeshComponent0' }] },
280
+ outputProps: {
281
+ hitCount: { type: 'number', description: 'How many lines hit something.' },
282
+ hits: {
283
+ type: 'array',
284
+ items: {
285
+ type: 'object', additionalProperties: false,
286
+ properties: {
287
+ hit: { type: 'boolean', description: 'Whether this line hit something that blocks the channel.' },
288
+ location: { type: 'object', additionalProperties: true, 'x-unreal-reflection-boundary': true, description: 'Where the line first hits, {x, y, z} in world space.' },
289
+ normal: { type: 'object', additionalProperties: true, 'x-unreal-reflection-boundary': true, description: 'Surface normal there.' },
290
+ distance: { type: 'number', description: 'Distance from start, in cm.' },
291
+ actorName: { type: 'string', description: 'Label of the actor hit.' },
292
+ componentName: { type: 'string', description: 'Component whose collision the line hit.' },
293
+ startedInside: { type: 'boolean', description: 'True when start was already inside the collision it reports: the line begins in a wall, or in a pawn left out of ignoreActors.' },
294
+ },
295
+ },
296
+ description: 'One entry per line, in order.',
297
+ },
298
+ },
299
+ }),
135
300
  buildCoreRecord({
136
301
  parentTool: 'inspect', action: 'get_texture_details', dispatchAction: 'inspect_object', domain: D, family: 'object',
137
302
  summary: 'Inspect a texture asset: dimensions, format and settings. Any other object is refused with TYPE_MISMATCH.',
@@ -168,6 +333,7 @@ export const OBJECT_PROPERTY_RECORDS = [
168
333
  }),
169
334
  buildCoreRecord({
170
335
  parentTool: 'inspect', action: 'get_property', dispatchAction: 'get_property', domain: D, family: 'property',
336
+ editorStates: ANY_EDITOR_STATE,
171
337
  topics: ['read property', 'property value', 'get value', 'read field', 'actor property', 'object property', 'game instance variable', 'read live widget property', 'widget value during play'],
172
338
  summary: 'Read a property value from a world actor, asset, or Blueprint class defaults (blueprintPath, or the Blueprint asset\'s own path), or several at once (propertyNames).',
173
339
  whenToUse: ['A single property value must be read.', 'Several properties of one target must be read in one call (propertyNames).'],
@@ -182,17 +348,26 @@ export const OBJECT_PROPERTY_RECORDS = [
182
348
  exampleInput: { action: 'get_property', objectPath: '/Game/Maps/Demo.Demo_PersistentLevel.PlayerStart_1', propertyName: 'ActorLabel' },
183
349
  exampleOutput: { success: true, message: 'Property read', value: 'PlayerStart_1' },
184
350
  outputProps: {
185
- value: P.value,
186
- properties: { type: 'array', items: { type: 'object', additionalProperties: true, 'x-unreal-reflection-boundary': true }, 'x-unreal-reflection-boundary': true, description: 'With propertyNames: each property that resolved, as {propertyName (its own spelling), value}.' },
351
+ value: P.valueRead,
352
+ properties: { type: 'array', items: { type: 'object', additionalProperties: true, 'x-unreal-reflection-boundary': true }, 'x-unreal-reflection-boundary': true, description: 'With propertyNames: each property that resolved, as {propertyName (its own spelling), value}, plus propertyPath (the path as asked) when that differs, so two .Text reads stay apart.' },
187
353
  missingProperties: { type: 'array', items: { type: 'string' }, description: 'With propertyNames: each name that did not resolve, as "Name: reason".' },
188
354
  },
189
355
  }),
190
356
  buildCoreRecord({
191
357
  parentTool: 'inspect', action: 'set_property', dispatchAction: 'set_property', domain: D, family: 'property',
358
+ editorStates: ANY_EDITOR_STATE,
192
359
  topics: ['write property', 'set value', 'change property', 'modify property', 'edit property', 'set field', 'set property on actor', 'set game instance variable'],
193
- summary: 'Write a property value on a world actor, asset, or Blueprint CDO, or several at once (properties).',
194
- whenToUse: ['A single property value must be written.', 'Several properties of one target must be written in one call (properties).'],
195
- whenNotToUse: ['The property is read-only or the target is a packed asset.'],
360
+ summary: 'Write a property value on a world actor, asset, or Blueprint CDO; several at once (properties), or the same on several objects (objectPaths).',
361
+ whenToUse: [
362
+ 'A single property value must be written.',
363
+ 'Several properties of one target must be written in one call (properties).',
364
+ 'The same property must be written on several objects in one call (objectPaths), e.g. one volume on every ambient sound.',
365
+ 'A placed actor\'s component property must be written ("StaticMeshComponent0.StaticMesh", "Mesh.BodyInstance.CollisionEnabled"): a StaticMesh, collision, SimulatePhysics or Mobility key goes through the component\'s own setter, as control_actor edit_component writes it.',
366
+ ],
367
+ whenNotToUse: [
368
+ 'The property is read-only or the target is a packed asset.',
369
+ 'A value the object works out from another of its settings: the object puts the write back and the call fails with PROPERTY_SET_FAILED naming both values, so change that setting instead. A value the object only adjusts (a clamp) is written, and the message names what it became.',
370
+ ],
196
371
  inputProps: {
197
372
  objectPath: P.runtimeObjectPath, actorName: P.actorName, name: P.name, blueprintPath: P.blueprintPath, propertyName: P.propertyName, propertyPath: P.propertyPath, value: P.value, markDirty: P.markDirty,
198
373
  properties: {
@@ -201,16 +376,16 @@ export const OBJECT_PROPERTY_RECORDS = [
201
376
  'x-unreal-reflection-boundary': true,
202
377
  description: 'Several writes on the same target in place of propertyName and value: {name or dotted path: value}, e.g. {"BoxExtent": {"X": 20, "Y": 90, "Z": 100}, "CollisionProfileName": "OverlapAllDynamic"}. Each is written as a single call would be and reported under properties; the call fails naming any that did not apply. watch applies to single writes only.',
203
378
  },
379
+ objectPaths: { type: 'array', items: { type: 'string' }, minItems: 1, maxItems: 64, description: 'The same write on several targets in one call, in place of objectPath: each entry is a target as objectPath takes it (an actor, "Actor.Component", an asset, a class default), written as a single call would be, with properties too. Answered under targets; the call fails naming each target that did not take the write, and the others keep it.' },
204
380
  watch: {
205
381
  type: 'object',
206
382
  description: 'Sample a property right after the write, in the same call, to see what the write sets off before it ends: a HUD pop or fade runs on real time, even with PIE paused, and is over before a later call can read it. The reply lists each value the property took with its time t in seconds (Lives on GameInstance with watch {objectPath: "WBP_HUD", propertyName: "LivesBox.RenderTransform"}).',
207
383
  properties: {
208
- propertyName: { type: 'string', description: 'The property to sample; a dotted path reaches through widgets and structs (LivesBox.RenderTransform).' },
384
+ propertyName: { type: 'string', description: 'The property to sample; a dotted path reaches through widgets and structs (LivesBox.RenderTransform). Omitted, the property the call writes.' },
209
385
  objectPath: { type: 'string', description: 'The object to sample, as objectPath takes it (GameInstance, a live widget such as WBP_HUD); omitted, the object written.' },
210
386
  durationSeconds: { type: 'number', description: 'Real seconds to sample (default 0.5, at most 20).' },
211
387
  intervalSeconds: { type: 'number', description: 'Seconds between samples (default 0: every frame); a sample is kept only when the value changed.' },
212
388
  },
213
- required: ['propertyName'],
214
389
  additionalProperties: false,
215
390
  },
216
391
  },
@@ -220,8 +395,11 @@ export const OBJECT_PROPERTY_RECORDS = [
220
395
  outputProps: {
221
396
  watch: { type: 'object', additionalProperties: true, 'x-unreal-reflection-boundary': true, description: 'With watch: objectPath, propertyName, samples ({t, value}, kept when the value changed), sampleCount, changed (it took more than one value) and frames.' },
222
397
  properties: { type: 'array', items: { type: 'object', additionalProperties: true, 'x-unreal-reflection-boundary': true }, 'x-unreal-reflection-boundary': true, description: 'With properties: each write as {propertyName, applied, value read back}.' },
223
- applied: { type: 'number', description: 'With properties: how many writes applied.' },
224
- instancesUpdated: { type: 'number', description: 'A write to a class default or component template: how many placed copies that still held the old default took the new one, as the details panel does; copies that override the value keep theirs. Absent for any other target.' },
398
+ targets: { type: 'array', items: { type: 'object', additionalProperties: true, 'x-unreal-reflection-boundary': true }, 'x-unreal-reflection-boundary': true, description: 'With objectPaths: each target as {objectPath as given, applied, the value read back (properties with properties), actorPath, saved, saveSkippedReason}.' },
399
+ applied: { type: 'number', description: 'With properties: how many writes applied; with objectPaths: how many targets.' },
400
+ saved: { type: 'boolean', description: 'Whether the target\'s package was written to disk after the write; with properties, the last write\'s save, which holds every write of the call. False with saveSkippedReason when nothing was saved.' },
401
+ saveSkippedReason: { type: 'string', description: 'Why nothing was saved: markDirty was false, a running-game or transient object (the change lasts until PIE stops), level content (saved with its level), engine content, or a save that failed (the change is only in memory).' },
402
+ instancesUpdated: { type: 'number', description: 'A write to a class default or component template: how many placed copies, and derived Blueprints\' defaults with their own copies, still held the old default and took the new one, as the details panel does; one that overrides the value keeps it. Absent for any other target.' },
225
403
  blueprintCompiled: { type: 'boolean', description: 'True when the target was a Blueprint CDO and the Blueprint was recompiled, so the value now applies to newly spawned instances. The reply (value, actorPath, actorClass) is read back from the recompiled class default object and the Blueprint package is saved; a variable the Blueprint declares keeps the value as its default, and the call fails with PROPERTY_SET_FAILED, never success, when the compile did not keep it. A Default__ objectPath resolves to the Blueprint\'s current default object; a copy a compile left behind (a REINST_ class, or a Blueprint object in /Engine/Transient with no game running) fails with STALE_TARGET. False for plain world actors and assets, where no compile is involved.' },
226
404
  materialRebuilt: { type: 'boolean', description: 'True when the target lives inside a material or material function (a material expression such as a TextureObjectParameter, as <material>.<material>:<nodeName>) and that material was rebuilt, so its parameter lists, which every material instance reads, show the write at once (a new ParameterName, a new default Texture). Absent for any other target.' },
227
405
  },
@@ -19,6 +19,7 @@ export declare const P: {
19
19
  readonly propertyNames: JsonObject;
20
20
  readonly componentNames: JsonObject;
21
21
  readonly value: Prop;
22
+ readonly valueRead: Prop;
22
23
  readonly structPath: JsonObject;
23
24
  readonly limit: JsonObject;
24
25
  readonly offset: JsonObject;
@@ -6,7 +6,7 @@ export const P = {
6
6
  actorName: str('World actor name to inspect.'),
7
7
  actorNames: arrStr('Several world actors to act on in one call, in place of actorName; names not found are listed back.'),
8
8
  name: str('Actor name identifier (alias of actorName).'),
9
- propertyName: str('Property to read or write: a name, or a dotted path through structs at any depth (BodyInstance.CollisionEnabled). A bare name that is not on the object itself resolves to the one struct member carrying it. Give this or propertyPath.'),
9
+ propertyName: str('Property to read or write: a name, or a dotted path through structs and object references at any depth (BodyInstance.CollisionEnabled). On an actor the path may start with a component\'s name, including one added in the level (MyAudio.VolumeMultiplier). A bare name that is not on the object itself resolves to the one struct member carrying it. Give this or propertyPath.'),
10
10
  propertyPath: str('Same as propertyName (a name or dotted path); used when propertyName is absent. Give this or propertyName.'),
11
11
  componentName: str('Component name on the actor.'),
12
12
  className: str('Class name or /Script/ class path to inspect.'),
@@ -19,6 +19,7 @@ export const P = {
19
19
  propertyNames: arrStr('Specific property names to include.'),
20
20
  componentNames: arrStr('Specific component names to include.'),
21
21
  value: { description: 'Property value to set (type depends on the target property).' },
22
+ valueRead: { description: 'The value read; its type follows the property.' },
22
23
  structPath: str('UserDefinedStruct asset /Game path to introspect.'),
23
24
  limit: num('Maximum objects to return (default 100).'),
24
25
  offset: num('Matching objects to skip before the first one returned, for paging (default 0); hasMore says whether more follow.'),
@@ -1,4 +1,4 @@
1
- import { buildCoreRecord } from '../core/builder.js';
1
+ import { ANY_EDITOR_STATE, buildCoreRecord } from '../core/builder.js';
2
2
  import { P } from './properties.js';
3
3
  const D = 'inspect';
4
4
  const RUNTIME_REPORT_OUTPUT = {
@@ -17,6 +17,7 @@ const RUNTIME_REPORT_OUTPUT = {
17
17
  export const RUNTIME_RECORDS = [
18
18
  buildCoreRecord({
19
19
  parentTool: 'inspect', action: 'runtime_report', dispatchAction: 'runtime_report', domain: D, family: 'runtime',
20
+ editorStates: ANY_EDITOR_STATE,
20
21
  summary: 'Return a runtime report for the current PIE/simulate session.',
21
22
  whenToUse: ['Runtime state of actors/components/properties must be inspected during PIE.', 'PIE-only runtime state must be inspected.'],
22
23
  whenNotToUse: ['The editor is not in PIE; the report will be empty.'],
@@ -1,8 +1,9 @@
1
- import { buildCoreRecord } from '../core/builder.js';
1
+ import { ANY_EDITOR_STATE, buildCoreRecord } from '../core/builder.js';
2
2
  const D = 'inspect';
3
3
  export const STATS_RECORDS = [
4
4
  buildCoreRecord({
5
5
  parentTool: 'inspect', action: 'get_scene_stats', dispatchAction: 'get_scene_stats', domain: D, family: 'stats',
6
+ editorStates: ANY_EDITOR_STATE,
6
7
  summary: 'Return scene statistics (actor counts, component counts, etc.).',
7
8
  whenToUse: ['Scene-level statistics must be inspected.'],
8
9
  whenNotToUse: ['Runtime performance is needed; use get_performance_stats.'],
@@ -15,6 +16,7 @@ export const STATS_RECORDS = [
15
16
  }),
16
17
  buildCoreRecord({
17
18
  parentTool: 'inspect', action: 'get_performance_stats', dispatchAction: 'get_performance_stats', domain: D, family: 'stats',
19
+ editorStates: ANY_EDITOR_STATE,
18
20
  summary: 'Return performance statistics (frame rate, frame time, game, render and GPU thread times, actor count).',
19
21
  whenToUse: ['Performance metrics must be inspected.'],
20
22
  whenNotToUse: ['Scene composition is needed; use get_scene_stats.'],
@@ -38,7 +40,8 @@ export const STATS_RECORDS = [
38
40
  frameTimeMs: { type: 'number', description: 'Frame time in milliseconds.' },
39
41
  estimatedFps: { type: 'number', description: 'Estimated frames per second from delta time.' },
40
42
  fps: { type: 'number', description: 'Reported frames per second (same as estimatedFps).' },
41
- gameThreadMs: { type: 'number', description: 'Game thread time in milliseconds.' },
43
+ gameThreadMs: { type: 'number', description: 'Game thread time in milliseconds; while Play In Editor runs it also holds the editor\'s own UI and viewports, so worldTickMs is the game\'s share.' },
44
+ worldTickMs: { type: 'number', description: 'The measured world\'s own tick in milliseconds (actors, components and physics, from the start of its tick to the end of its tick groups), averaged over about 30 frames: the game\'s share of gameThreadMs. Absent until the world has ticked.' },
42
45
  renderThreadMs: { type: 'number', description: 'Render thread time in milliseconds.' },
43
46
  rhiThreadMs: { type: 'number', description: 'RHI thread time in milliseconds.' },
44
47
  gpuMs: { type: 'number', description: 'GPU frame time in milliseconds.' },
@@ -49,6 +52,7 @@ export const STATS_RECORDS = [
49
52
  }),
50
53
  buildCoreRecord({
51
54
  parentTool: 'inspect', action: 'get_memory_stats', dispatchAction: 'get_memory_stats', domain: D, family: 'stats',
55
+ editorStates: ANY_EDITOR_STATE,
52
56
  summary: 'Return memory statistics (allocated, virtual, resource counts).',
53
57
  whenToUse: ['Memory usage must be inspected.'],
54
58
  whenNotToUse: ['Performance timing is needed; use get_performance_stats.'],