isle-portable/extensions/src/multiplayer/protocol.cpp
foxtacles 37b328a595
3rd person camera (#3)
* Add feasibility plan for reusing multiplayer animation system for third-person camera

Evaluates reusing the multiplayer extension's RemotePlayer animation system
(BuildROIMap, AssignROIIndices, ApplyAnimationTransformation) for the local
player to enable a third-person camera mode. Conclusion: feasible with only
3 single-line extension hooks added to core game code.

https://claude.ai/code/session_01NC3zdQZ4nqEcYjyvStqcdD

* WIP: Third-person camera with animation reuse and movement fix

* Fix third-person camera bugs: vehicles, remote facing, emote distortion (#2)

- Fix spawn pose and building re-entry by applying idle frame 0 and
  reinitializing on world enable
- Handle vehicle transitions: ride animations for small vehicles,
  first-person fallback for large vehicles and helicopter
- Keep vehicle dashboards visible for exit controls
- Disable third-person camera for large vehicles, fix ROI cleanup
- Move HandleActorExit hook to end of Exit() for immediate reinit
- Fix remote player facing 180 degrees wrong by negating direction
  in BroadcastLocalState when third-person camera is active
- Fix Hat Tip emote distortion from compounding transform scale by
  saving clean parent transform at emote start and restoring after
  each frame's animation application

* DRY cleanup for third-person camera branch

- Extract shared DetectVehicleType() to protocol.h/cpp (was duplicated
  in ThirdPersonCamera and NetworkManager)
- Remove no-op HandlePostApplyTransform hook chain (called every frame
  for every LegoPathActor but did nothing)
- Add ThirdPersonCamera::ClearAnimCaches() helper (pattern repeated 5x)
- Add AnimUtils::EnsureROIMapVisibility() inline helper (loop repeated
  5x across ThirdPersonCamera and RemotePlayer)
- Remove redundant static_cast in multiplayer.cpp (UserActor() already
  returns LegoPathActor*)
- Delete THIRD_PERSON_CAMERA_ANIMATION_REUSE_PLAN.md development artifact
2026-03-07 01:38:45 +01:00

80 lines
2.2 KiB
C++

#include "extensions/multiplayer/protocol.h"
#include "legopathactor.h"
#include <cstddef>
namespace Multiplayer
{
const char* const g_walkAnimNames[] = {
"CNs001xx", // 0: Normal (default)
"CNs002xx", // 1: Joyful
"CNs003xx", // 2: Gloomy
"CNs005xx", // 3: Leaning
"CNs006xx", // 4: Scared
"CNs007xx", // 5: Hyper
};
const int g_walkAnimCount = sizeof(g_walkAnimNames) / sizeof(g_walkAnimNames[0]);
const char* const g_idleAnimNames[] = {
"CNs008xx", // 0: Sway (default)
"CNs009xx", // 1: Groove
"CNs010xx", // 2: Excited
};
const int g_idleAnimCount = sizeof(g_idleAnimNames) / sizeof(g_idleAnimNames[0]);
const char* const g_emoteAnimNames[] = {
"CNs011xx", // 0: Wave
"CNs012xx", // 1: Hat Tip
};
const int g_emoteAnimCount = sizeof(g_emoteAnimNames) / sizeof(g_emoteAnimNames[0]);
// Vehicle model names (LOD names). The helicopter is a compound ROI ("copter")
// with no standalone LOD; use its body part instead.
const char* const g_vehicleROINames[VEHICLE_COUNT] =
{"chtrbody", "jsuser", "dunebugy", "bike", "board", "moto", "towtk", "ambul"};
// Ride animation names for small vehicles (NULL = large vehicle, no ride anim)
const char* const g_rideAnimNames[VEHICLE_COUNT] =
{NULL, NULL, NULL, "CNs001Bd", "CNs001sk", "CNs011Ni", NULL, NULL};
// Vehicle variant ROI names used in ride animations
const char* const g_rideVehicleROINames[VEHICLE_COUNT] =
{NULL, NULL, NULL, "bikebd", "board", "motoni", NULL, NULL};
bool IsLargeVehicle(int8_t p_vehicleType)
{
return p_vehicleType != VEHICLE_NONE && p_vehicleType < VEHICLE_COUNT && g_rideAnimNames[p_vehicleType] == NULL;
}
int8_t DetectVehicleType(LegoPathActor* p_actor)
{
static const struct {
const char* className;
int8_t vehicleType;
} vehicleMap[] = {
{"Helicopter", VEHICLE_HELICOPTER},
{"Jetski", VEHICLE_JETSKI},
{"DuneBuggy", VEHICLE_DUNEBUGGY},
{"Bike", VEHICLE_BIKE},
{"SkateBoard", VEHICLE_SKATEBOARD},
{"Motorcycle", VEHICLE_MOTOCYCLE},
{"TowTrack", VEHICLE_TOWTRACK},
{"Ambulance", VEHICLE_AMBULANCE},
};
if (!p_actor) {
return VEHICLE_NONE;
}
for (const auto& entry : vehicleMap) {
if (p_actor->IsA(entry.className)) {
return entry.vehicleType;
}
}
return VEHICLE_NONE;
}
} // namespace Multiplayer