Place/remove (task #21): all four block types (Hull/Thruster/Power/Storage)
now have working place chains on BP_PlayerCharacter (keys 1-4), plus a
remove key (X) that raycasts to a targeted block, verifies it via
Array_Contains against PlacedBlocks before destroying it (guards against
deleting terrain/ship/other actors), and calls RemoveBlockFromArray.
Stat aggregation (task #22): rewrote RecalculateStats on BP_ShipPawn to
iterate PlacedBlocks with a ForEachLoop, casting each element to its block
class and summing bonuses onto base values, instead of reading four fixed
Slot_* variables. Multiple blocks of the same type now correctly stack
(e.g. two Hull blocks = +100, not capped at one). Also fixed a real bug
found during manual playtesting: AddBlockToArray/RemoveBlockFromArray never
actually called RecalculateStats (dead-end exec pins), so stats never
updated after the initial BeginPlay call regardless of what was placed.
Ship-relative placement: raycast hit location is now converted into the
ship's local space, clamped to a 600-unit radius, snapped to the 200-unit
grid, then converted back to world space. Recomputed every tick from the
ship's current transform, so placement always stays near/aligned with the
ship instead of landing at arbitrary world coordinates. Also fixed a
related bug: the raycast never checked bBlockingHit, so a miss (e.g. aiming
at open sky) silently fell back to world origin (0,0,0) -- coincidentally
the ship's spawn point -- making blocks appear to "teleport" there and then
trail behind once the ship moved. Added bHasValidBuildTarget, gating all
four placement branches on an actual hit.
Mouse-look: extended the UnrealMCPython plugin itself (hot-patched via
Live Coding) with a new "InputAxisKey" node type wrapping
UK2Node_InputAxisKeyEvent::Initialize(FKey), since the existing "InputKey"
node type only supports press/release button semantics, not continuous
axis values. Wired Mouse X/Y to AddControllerYawInput/AddControllerPitchInput
(Y inverted), switched bUseControllerRotationYaw/Pitch back on, and
repurposed the old A/D actor-rotation logic into A/D strafe via
AddMovementInput(RightVector). Also added mouse-wheel zoom on the ship's
CameraBoom (TargetArmLength, clamped 300-2000).
Visual polish: ship mesh was a single scaled Cone; added a cylinder
fuselage and two wing panels for a recognizable ship silhouette. Added
distinct colored materials per block type (Hull/Thruster/Power/Storage)
plus a translucent ghost-preview material, replacing the default gray
checker look. Added a SkyAtmosphere actor to fix the "will render black"
sky light warning that made the level unreadable.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Real root cause, found after two incorrect attempts: ConnectBlueprintPins
always instantiated the generic UK2Node_CallFunction for every CallFunction
node, including wildcard array library functions like Array_Add and
Array_RemoveItem. Only UK2Node_CallArrayFunction (a UK2Node_CallFunction
subclass) overrides NotifyPinConnectionListChanged to resolve its wildcard
TargetArray/Item pins from the connected array's type -- the generic base
class has a no-op default, so no connection-notification of any kind could
ever have fixed it. This also explains why the ForEachLoop fix (a
MacroInstance, unrelated class hierarchy) worked while this didn't, despite
looking like the same "wildcard pin" symptom.
Debugging trail for the record: first tried NodeConnectionListChanged()
(wrong function, no args -- happened to be irrelevant to both bugs and
coincidentally look like it fixed ForEachLoop). Then tried adding
ReconstructNode() (no effect). Then matched the schema's actual
TryCreateConnection code exactly (PinConnectionListChanged(Pin) with the
specific pin) -- correct in general (this IS what fixes MacroInstance
wildcards, confirmed via UE_LOG marker showing the call executes), but
insufficient here because the node class itself was wrong.
Fix: check TargetFunc->HasMetaData(TEXT("ArrayParm")) (the same meta key
UK2Node_CallArrayFunction itself checks) and instantiate the correct class
before calling SetFromFunction(). Verified: Array_Add's TargetArray/NewItem
pins now show concrete "object/Actor" type instead of wildcard, and the
containing Blueprint compiles.
UK2Node_SpawnActorFromClass::PostPlacedNewNode() (engine source)
overrides the base class without calling Super::PostPlacedNewNode(),
and unconditionally dereferences GetScaleMethodPin() via
FindPinChecked -- but that pin is only created in
AllocateDefaultPins(), which FGraphNodeCreator::Finalize() always
calls *after* PostPlacedNewNode(). Result: a hard assertion failure
(EdGraphNode.h:586) that crashes the whole editor, every time,
regardless of any node_json parameters.
The real editor UI never hits this because it creates nodes through
a different path (node spawner templates) that doesn't have this
ordering problem. Our plugin uses the generic FGraphNodeCreator
pattern directly, so it does.
Fix: manually call SpawnNode->AllocateDefaultPins() before
Creator.Finalize(), so pins already exist by the time
PostPlacedNewNode() runs. Verified: SpawnActor node now creates
cleanly with all expected pins (Class, SpawnTransform, etc), editor
stayed stable and MCP connection alive afterward.
Rebuilt via Live Coding successfully this time (Ctrl+Alt+F11) --
the earlier UbaCli spawn failure appears to have been a one-off,
possibly from a conflicting concurrent Build.bat invocation.
ConnectBlueprintPins called SourcePin->MakeLinkTo(TargetPin) but never
notified either node of the new connection, so wildcard-typed pins
(MacroInstance nodes like ForEachLoop, and by extension any wildcard
array/map library call) never resolved their type -- compilation
failed with "type ... is undetermined" regardless of how many times
you recompiled. The real graph editor triggers this via the schema's
TryCreateConnection; MakeLinkTo alone doesn't.
Fix: call NodeConnectionListChanged() on both nodes after linking,
matching what the schema does. Verified against the exact ForEachLoop
scenario that failed twice before the fix -- the Array pin now
resolves to a concrete type and the graph compiles.
Rebuilt via full offline Build.bat (UnrealEditor target) rather than
Live Coding -- Live Coding's patch-link step hit a persistent
"Could not spawn process UbaCli.exe (Error 267)" in this environment,
unrelated to the code change itself (the .cpp compiled cleanly both
times; only the hot-patch link step failed).