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>
PlacedBlocks array + PendingBlockRef variable + two CustomEvents that
append/remove PendingBlockRef via the now-fixed Array_Add/
Array_RemoveItem. Verified compiling with fully-resolved (non-
wildcard) pin types. Not yet wired to any place/remove key input --
that's the remaining part of task #21.
Four typed slot variables (Slot_Hull/Thruster/Power/Storage, one per
block class) plus a RecalculateStats event that reads each slot (via
IsValid + external-member Get on the slot's block instance) and
derives MaxHull, ShipMovement.MaxSpeed, MaxPower/Power, and
StorageCapacity from base values plus whatever's attached. Called at
the end of BeginPlay. All slots empty = base values, matching current
gameplay exactly.
New technique discovered: VariableGet/VariableSet support an external
member reference via a "variable_class" field (full asset path
required for Blueprint-generated classes, not just the short class
name) -- this is how block-instance properties and component
properties (e.g. FloatingPawnMovement.MaxSpeed) get read/written at
runtime without needing a formal Function or the broken array/loop
path.
ResourceNode_01 placed in the landing zone. E key on the character:
checks distance to the resource node (via a typed ResourceNodeRef
variable, same object-reference technique as the ship/character
cross-refs), and if within 300 units calls AddStorage on the ship
(through ShipPawnRef) and destroys the node. AddStorage increments
StorageAmount up to StorageCapacity, printing feedback either way.
Smoke-tested in PIE: no runtime errors.
F key on the ship: teleports the player character to just above the
ship's location and possesses it. F key on the character: possesses
the ship back. Same controller, so possessing one pawn automatically
unpossesses the other.
Notable implementation detail: GameplayStatics::GetActorOfClass
looked like the obvious way to find "the other pawn" at runtime, but
this MCP plugin's pin_defaults mechanism can't correctly set a Class-
type pin (it accepts the string silently but the compiler later
rejects it as an invalid default) -- there's no supported node type
for a class-literal either. Worked around it by adding real object-
reference variables (PlayerCharacterRef / ShipPawnRef) via
unreal.BlueprintEditorLibrary.get_object_reference_type() directly
through the Python escape hatch, marking them instance-editable, and
wiring the actual actor references between the two placed level
instances. Avoids needing any runtime class lookup at all.
DamageHull and RepairHull custom events (this MCP plugin's Blueprint
graph builder can't author formal parameterized Functions yet, only
CustomEvents, so amounts are fixed rather than passed in):
- DamageHull: Hull -= 10, clamped to 0.
- RepairHull: if StorageAmount > 0, consumes 1 storage and restores
20 hull (clamped to MaxHull); otherwise no-ops.
Bound to H/R test keys with on-screen PrintString feedback, since
there's no HUD yet to show the actual Hull/Storage values.
No real damage trigger (e.g. collision) wired up yet -- nothing to
collide with in the level yet. Smoke-tested in PIE: no runtime
errors.
CameraBoom was parented to ShipMesh, which is rotated 90deg to point
the placeholder cone forward -- the camera inherited that rotation,
producing a badly skewed near-edge-on view. Reparented CameraBoom to
the actor root instead, so its pitch is relative to actor-forward,
not the mesh's cosmetic rotation.
Also added 5 marker cubes around the spawn point: the level was
completely empty, so with a rigidly-attached chase camera, forward/
backward translation had zero visual reference and could look like
nothing was happening even if movement was working correctly.
W/S thrust forward/back, A/D yaw left/right, via direct key-binding
(InputKey nodes) rather than Enhanced Input -- this MCP plugin's
Blueprint graph builder doesn't support authoring Enhanced Input
Action nodes yet, only raw key events. Tick applies ThrustAxis/
YawAxis each frame via AddMovementInput/AddActorLocalRotation.
Bumped FloatingPawnMovement Max Speed/Acceleration for a snappier
placeholder feel. Smoke-tested in PIE: no runtime errors.
BP_ShipPawn: cone mesh, FloatingPawnMovement for gravity-free flight,
spring-arm chase camera. Placed in L_TestFlight and set to
auto-possess Player 0 for PIE testing. No input wiring yet.