mirror of
https://github.com/isledecomp/isle-portable.git
synced 2026-05-02 02:23:56 +00:00
- Reduce pinch zoom sensitivity (15x → 6x multiplier) - Add cumulative deadzone threshold for 1st/3rd person transitions to prevent accidental mode switches from slight finger movement - Preserve camera touch tracking through 3rd→1st transition so the same fingers can pinch back without lifting (seamless round-trip) - On 1st→3rd transition, selectively clear only camera-owned fingers from LegoInputManager's touch scheme state, preserving any active left-side movement finger - Suppress camera gesture processing until finger positions re-sync after transition to prevent camera jumps from stale coordinates - Hide local player name bubble when transitioning to 1st person, restore on transition back to 3rd person Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
262 lines
6.0 KiB
C++
262 lines
6.0 KiB
C++
#include "extensions/thirdpersoncamera.h"
|
|
|
|
#include "extensions/common/charactercustomizer.h"
|
|
#include "extensions/thirdpersoncamera/controller.h"
|
|
#include "islepathactor.h"
|
|
#include "legoeventnotificationparam.h"
|
|
#include "legoinputmanager.h"
|
|
#include "legonavcontroller.h"
|
|
#include "legopathactor.h"
|
|
#include "legovideomanager.h"
|
|
#include "misc.h"
|
|
#include "mxcore.h"
|
|
#include "mxmisc.h"
|
|
#include "mxticklemanager.h"
|
|
#include "realtime/vector.h"
|
|
#include "roi/legoroi.h"
|
|
|
|
#include <SDL3/SDL_stdinc.h>
|
|
|
|
using namespace Extensions;
|
|
using namespace Extensions::Common;
|
|
|
|
std::map<std::string, std::string> ThirdPersonCameraExt::options;
|
|
bool ThirdPersonCameraExt::enabled = false;
|
|
ThirdPersonCamera::Controller* ThirdPersonCameraExt::s_camera = nullptr;
|
|
bool ThirdPersonCameraExt::s_registered = false;
|
|
bool ThirdPersonCameraExt::s_inIsleWorld = false;
|
|
|
|
namespace Extensions
|
|
{
|
|
namespace ThirdPersonCamera
|
|
{
|
|
|
|
class TickleAdapter : public MxCore {
|
|
public:
|
|
TickleAdapter(Controller* p_camera) : m_camera(p_camera) {}
|
|
|
|
MxResult Tickle() override
|
|
{
|
|
if (m_camera) {
|
|
m_camera->Tick(0.016f);
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
const char* ClassName() const override { return "ThirdPersonCamera::TickleAdapter"; }
|
|
|
|
private:
|
|
Controller* m_camera;
|
|
};
|
|
|
|
} // namespace ThirdPersonCamera
|
|
} // namespace Extensions
|
|
|
|
static Extensions::ThirdPersonCamera::TickleAdapter* s_tickleAdapter = nullptr;
|
|
|
|
void ThirdPersonCameraExt::Initialize()
|
|
{
|
|
if (!s_camera) {
|
|
s_camera = new ThirdPersonCamera::Controller();
|
|
}
|
|
s_camera->Enable();
|
|
}
|
|
|
|
void ThirdPersonCameraExt::HandleCreate()
|
|
{
|
|
if (!s_registered && s_camera) {
|
|
s_tickleAdapter = new Extensions::ThirdPersonCamera::TickleAdapter(s_camera);
|
|
TickleManager()->RegisterClient(s_tickleAdapter, 10);
|
|
s_registered = true;
|
|
}
|
|
}
|
|
|
|
MxBool ThirdPersonCameraExt::HandleWorldEnable(LegoWorld* p_world, MxBool p_enable)
|
|
{
|
|
if (!s_camera) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (p_enable) {
|
|
s_camera->OnWorldEnabled(p_world);
|
|
s_inIsleWorld = true;
|
|
}
|
|
else {
|
|
s_camera->OnWorldDisabled(p_world);
|
|
s_inIsleWorld = false;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void ThirdPersonCameraExt::HandleActorEnter(IslePathActor* p_actor)
|
|
{
|
|
if (s_camera) {
|
|
s_camera->OnActorEnter(p_actor);
|
|
}
|
|
}
|
|
|
|
void ThirdPersonCameraExt::HandleActorExit(IslePathActor* p_actor)
|
|
{
|
|
if (s_camera) {
|
|
s_camera->OnActorExit(p_actor);
|
|
}
|
|
}
|
|
|
|
void ThirdPersonCameraExt::HandleCamAnimEnd(LegoPathActor* p_actor)
|
|
{
|
|
if (s_camera) {
|
|
s_camera->OnCamAnimEnd(p_actor);
|
|
}
|
|
}
|
|
|
|
void ThirdPersonCameraExt::OnSDLEvent(SDL_Event* p_event)
|
|
{
|
|
if (!s_camera || !s_inIsleWorld) {
|
|
return;
|
|
}
|
|
|
|
s_camera->HandleSDLEventImpl(p_event);
|
|
|
|
if (s_camera->ConsumeAutoDisable()) {
|
|
s_camera->Disable(/*p_preserveTouch=*/true);
|
|
}
|
|
else if (s_camera->ConsumeAutoEnable()) {
|
|
// Clear the movement system's touch state for camera-owned fingers only,
|
|
// so any virtual thumbstick input from 1st-person mode is zeroed while
|
|
// leaving a left-side movement finger intact.
|
|
LegoInputManager* im = InputManager();
|
|
if (im) {
|
|
for (int i = 0; i < s_camera->GetTouchCount(); i++) {
|
|
SDL_FingerID fid = s_camera->GetFingerID(i);
|
|
if (im->m_touchFinger == fid) {
|
|
im->m_touchFinger = 0;
|
|
im->m_touchVirtualThumb = {0, 0};
|
|
im->m_touchVirtualThumbOrigin = {0, 0};
|
|
}
|
|
im->m_touchFlags.erase(fid);
|
|
}
|
|
}
|
|
|
|
// Suppress camera gestures until finger positions re-sync to avoid
|
|
// a camera jump from stale positions carried through the transition.
|
|
s_camera->SuppressGestures();
|
|
|
|
s_camera->SetOrbitDistance(ThirdPersonCamera::Controller::MIN_DISTANCE);
|
|
s_camera->Enable();
|
|
}
|
|
}
|
|
|
|
MxBool ThirdPersonCameraExt::IsThirdPersonCameraActive()
|
|
{
|
|
if (s_camera && s_camera->IsActive()) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
MxBool ThirdPersonCameraExt::HandleTouchInput(SDL_Event* p_event)
|
|
{
|
|
if (!s_camera || !s_camera->IsActive()) {
|
|
return FALSE;
|
|
}
|
|
|
|
switch (p_event->type) {
|
|
case SDL_EVENT_FINGER_DOWN:
|
|
if (s_camera->TryClaimFinger(p_event->tfinger)) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
|
|
case SDL_EVENT_FINGER_MOTION:
|
|
if (s_camera->IsFingerTracked(p_event->tfinger.fingerID)) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
|
|
case SDL_EVENT_FINGER_UP:
|
|
case SDL_EVENT_FINGER_CANCELED:
|
|
if (s_camera->TryReleaseFinger(p_event->tfinger.fingerID)) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
MxBool ThirdPersonCameraExt::HandleNavOverride(
|
|
LegoNavController* p_nav,
|
|
const Vector3& p_curPos,
|
|
const Vector3& p_curDir,
|
|
Vector3& p_newPos,
|
|
Vector3& p_newDir,
|
|
float p_deltaTime
|
|
)
|
|
{
|
|
if (!s_camera) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!s_camera->IsActive()) {
|
|
return FALSE;
|
|
}
|
|
|
|
return s_camera->HandleCameraRelativeMovement(p_nav, p_curPos, p_curDir, p_newPos, p_newDir, p_deltaTime);
|
|
}
|
|
|
|
MxBool ThirdPersonCameraExt::HandleROIClick(LegoROI* p_rootROI, LegoEventNotificationParam& p_param)
|
|
{
|
|
if (!s_camera) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!s_camera->GetDisplayROI() || s_camera->GetDisplayROI() != p_rootROI) {
|
|
return FALSE;
|
|
}
|
|
|
|
uint8_t changeType;
|
|
int partIndex;
|
|
if (!CharacterCustomizer::ResolveClickChangeType(changeType, partIndex, p_param.GetROI())) {
|
|
return TRUE;
|
|
}
|
|
|
|
s_camera->ApplyCustomizeChange(changeType, static_cast<uint8_t>(partIndex >= 0 ? partIndex : 0xFF));
|
|
|
|
LegoROI* effectROI = s_camera->GetDisplayROI();
|
|
if (effectROI) {
|
|
CharacterCustomizer::PlayClickSound(effectROI, s_camera->GetCustomizeState(), changeType == CHANGE_MOOD);
|
|
if (!s_camera->IsInVehicle() && !s_camera->IsInMultiPartEmote()) {
|
|
s_camera->StopClickAnimation();
|
|
MxU32 clickAnimId = CharacterCustomizer::PlayClickAnimation(effectROI, s_camera->GetCustomizeState());
|
|
s_camera->SetClickAnimObjectId(clickAnimId);
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
MxBool ThirdPersonCameraExt::IsClonedCharacter(const char* p_name)
|
|
{
|
|
if (!s_camera) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (s_camera->GetDisplayROI() != nullptr && !SDL_strcasecmp(s_camera->GetDisplayROI()->GetName(), p_name)) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
ThirdPersonCamera::Controller* ThirdPersonCameraExt::GetCamera()
|
|
{
|
|
return s_camera;
|
|
}
|
|
|
|
void ThirdPersonCameraExt::HandleSDLEvent(SDL_Event* p_event)
|
|
{
|
|
Extension<ThirdPersonCameraExt>::Call(TP::HandleSDLEvent, p_event);
|
|
}
|