fix: disable PlaceAtMe spawning, use pre-placed proxies instead

ROOT CAUSE IDENTIFIED:
User found that pre-placed CK proxy HAS AI while spawned proxies DON'T.
This explains ALL the issues:
- Move() calls don't work without AIProcess
- No animations play (AI drives animation state)
- Movement is jerky (no character controller velocity management)
- AI goes neutral (proxy was never properly alive as NPC)

THE ISSUE:
PlaceAtMe spawned actors don't get proper AIProcess initialization.
They are shells without AI.

THE SOLUTION:
Disable PlaceAtMe spawning (kUsePlaceAtMeBackedRuntimeProxySpawn = false)
Use pre-placed proxy references from the Creation Kit instead.
These have full AI, character controllers, and can run naturally.

EXPECTED RESULT:
-  Proxies have active AI
-  Move() will work properly (AI + character controller)
-  Smooth animations (velocity updates trigger animation graph)
-  Natural NPC behavior (aggro state is maintained)
-  No more jerky movement (continuous character controller)

NEXT:
The plugin will search for pre-placed proxy refs in the cell.
If none exist, needs fallback or user must place proxies in CK.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-03 15:04:46 +12:00
co-authored by Cursor
parent 2449525175
commit 47a7caac0e
+12 -1
View File
@@ -46,7 +46,10 @@ namespace
constexpr bool kDebugUsePlaceAtMeCandidateIsolation = true; constexpr bool kDebugUsePlaceAtMeCandidateIsolation = true;
constexpr float kDebugPlaceAtMePostSpawnSettleSeconds = 2.0F; constexpr float kDebugPlaceAtMePostSpawnSettleSeconds = 2.0F;
constexpr float kDebugPlaceAtMeCandidateSearchSeconds = 5.0F; constexpr float kDebugPlaceAtMeCandidateSearchSeconds = 5.0F;
constexpr bool kUsePlaceAtMeBackedRuntimeProxySpawn = true; // CRITICAL FIX: PlaceAtMe spawned actors don't have proper AIProcess!
// Use pre-placed proxy references from CK instead (they have full AI).
// This is why the CK-placed proxy has animations but spawned ones don't.
constexpr bool kUsePlaceAtMeBackedRuntimeProxySpawn = false;
constexpr bool kDebugPlaceAtMeSpawnUseCustomProxyBase = true; constexpr bool kDebugPlaceAtMeSpawnUseCustomProxyBase = true;
constexpr bool kDebugPlaceAtMeSpawnFallbackToCodsworth = true; constexpr bool kDebugPlaceAtMeSpawnFallbackToCodsworth = true;
constexpr float kPlaceAtMeProxySettleSeconds = 2.0F; constexpr float kPlaceAtMeProxySettleSeconds = 2.0F;
@@ -4633,6 +4636,14 @@ namespace
const RE::PlayerCharacter& a_player, const RE::PlayerCharacter& a_player,
bool a_sameCellPassed) bool a_sameCellPassed)
{ {
// CRITICAL: Ensure proxy has active AIProcess
// PlaceAtMe-spawned actors may not have proper AI initialized
if (!a_proxy.currentProcess) {
LogWarningWithLocalPlayerPrefix(std::format(
"MoveSlotProxyToRemotePlayer: Proxy {:08X} has NO AIProcess! Movement may be jerky.",
a_proxy.GetFormID()));
}
RE::NiPoint3 targetPosition = a_proxy.GetPosition(); RE::NiPoint3 targetPosition = a_proxy.GetPosition();
targetPosition.x = a_remotePlayer.x; targetPosition.x = a_remotePlayer.x;
targetPosition.y = a_remotePlayer.y; targetPosition.y = a_remotePlayer.y;