summaryrefslogtreecommitdiff
path: root/indra/newview/llagent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llagent.cpp')
-rw-r--r--indra/newview/llagent.cpp513
1 files changed, 489 insertions, 24 deletions
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index b29d1706c4..72ed9f8c25 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -84,7 +84,6 @@
#include "llurlentry.h"
#include "llviewercontrol.h"
#include "llviewerdisplay.h"
-#include "llviewerjoystick.h"
#include "llviewermediafocus.h"
#include "llviewermenu.h"
#include "llviewerobjectlist.h"
@@ -114,6 +113,12 @@ const F32 AUTOPILOT_MIN_TARGET_HEIGHT_OFF_GROUND = 1.f; // meters
const F32 AUTOPILOT_MAX_TIME_NO_PROGRESS_WALK = 1.5f; // seconds
const F32 AUTOPILOT_MAX_TIME_NO_PROGRESS_FLY = 2.5f; // seconds. Flying is less presize, needs a bit more time
+// Ground-sit requests that do not originate from a click on the ground (e.g. the
+// game-control/keyboard "toggle sit" action) have no target position to check
+// against, so refuse them outright when the ground below the agent is farther
+// than this away, rather than snapping the agent down onto distant terrain.
+const F32 MAX_SIT_ON_GROUND_DISTANCE = 30.f; // meters
+
const F32 MAX_VELOCITY_AUTO_LAND_SQUARED = 4.f * 4.f;
const F64 CHAT_AGE_FAST_RATE = 3.0;
@@ -458,7 +463,7 @@ LLAgent::LLAgent() :
mCurrentFidget(0),
mFirstLogin(false),
mOutfitChosen(false),
-
+ mCrouch(false),
mVoiceConnected(false),
mMouselookModeInSignal(nullptr),
@@ -794,13 +799,17 @@ void LLAgent::moveUp(S32 direction)
mLastJumpInputTime = LLTimer::getTotalSeconds();
}
setControlFlags(AGENT_CONTROL_UP_POS | AGENT_CONTROL_FAST_UP);
+ mCrouch = false;
}
else if (direction < 0)
{
setControlFlags(AGENT_CONTROL_UP_NEG | AGENT_CONTROL_FAST_UP);
}
- gAgentCamera.resetView();
+ if (!mCrouch)
+ {
+ gAgentCamera.resetView();
+ }
}
//-----------------------------------------------------------------------------
@@ -851,6 +860,11 @@ void LLAgent::movePitch(F32 mag)
}
}
+bool LLAgent::isCrouching() const
+{
+ return mCrouch && !getFlying();
+}
+
// Does this parcel allow you to fly?
bool LLAgent::canFly()
{
@@ -918,6 +932,7 @@ void LLAgent::setFlying(bool fly, bool fail_sound)
{
add(LLStatViewer::FLY, 1);
}
+ mCrouch = false;
setControlFlags(AGENT_CONTROL_FLY);
}
else
@@ -1346,9 +1361,32 @@ LLVector3d LLAgent::getPosGlobalFromAgent(const LLVector3 &pos_agent) const
return pos_agent_d + mAgentOriginGlobal;
}
+static void finish_sit_on_ground(bool success, void*)
+{
+ if (success && mAllowedToSit)
+ {
+ gAgent.setFlying(false);
+ gAgent.clearControlFlags(AGENT_CONTROL_STAND_UP); // might have been set by autopilot
+ gAgent.setControlFlags(AGENT_CONTROL_SIT_ON_GROUND);
+ }
+}
+
void LLAgent::sitDown()
{
- if (mAllowedToSit) setControlFlags(AGENT_CONTROL_SIT_ON_GROUND);
+ // Sit on the ground directly beneath (or above) the agent's current position.
+ // Refuse if that spot is too far away, and otherwise walk/fly there first,
+ // rather than snapping straight down onto it (e.g. terrain far below a flying
+ // agent) as AGENT_CONTROL_SIT_ON_GROUND would do if sent immediately.
+ LLVector3d pos_global = getPositionGlobal();
+ LLVector3d sit_spot_global = pos_global;
+ sit_spot_global.mdV[VZ] = LLWorld::getInstance()->resolveLandHeightGlobal(pos_global);
+
+ if (dist_vec(pos_global, sit_spot_global) > MAX_SIT_ON_GROUND_DISTANCE)
+ {
+ return;
+ }
+
+ startAutoPilotGlobal(sit_spot_global, "Sit", NULL, finish_sit_on_ground, NULL, 0.7f);
}
//-----------------------------------------------------------------------------
@@ -1447,7 +1485,7 @@ LLVector3 LLAgent::getReferenceUpVector()
return up_vector;
}
-// Radians, positive is forward into ground
+// Radians, positive is downward toward ground
//-----------------------------------------------------------------------------
// pitch()
//-----------------------------------------------------------------------------
@@ -1461,27 +1499,23 @@ void LLAgent::pitch(F32 angle)
LLVector3 skyward = getReferenceUpVector();
+ // SL-19286 Avatar is upside down when viewed from below
+ // after left-clicking the mouse on the avatar and dragging down
+ //
+ // The issue is observed on angle below 10 degrees
+ const F32 look_down_limit = 179.f * DEG_TO_RAD;
+ const F32 look_up_limit = 10.f * DEG_TO_RAD;
+
+ F32 angle_from_skyward = acos(mFrameAgent.getAtAxis() * skyward);
+
// clamp pitch to limits
- if (angle >= 0.f)
+ if ((angle >= 0.f) && (angle_from_skyward + angle > look_down_limit))
{
- const F32 look_down_limit = 179.f * DEG_TO_RAD;
- F32 angle_from_skyward = acos(mFrameAgent.getAtAxis() * skyward);
- if (angle_from_skyward + angle > look_down_limit)
- {
- angle = look_down_limit - angle_from_skyward;
- }
+ angle = look_down_limit - angle_from_skyward;
}
- else if (angle < 0.f)
+ else if ((angle < 0.f) && (angle_from_skyward + angle < look_up_limit))
{
- const F32 look_up_limit = 5.f * DEG_TO_RAD;
- const LLVector3& viewer_camera_pos = LLViewerCamera::getInstance()->getOrigin();
- LLVector3 agent_focus_pos = getPosAgentFromGlobal(gAgentCamera.calcFocusPositionTargetGlobal());
- LLVector3 look_dir = agent_focus_pos - viewer_camera_pos;
- F32 angle_from_skyward = angle_between(look_dir, skyward);
- if (angle_from_skyward + angle < look_up_limit)
- {
- angle = look_up_limit - angle_from_skyward;
- }
+ angle = look_up_limit - angle_from_skyward;
}
if (fabs(angle) > 1e-4)
@@ -2134,7 +2168,7 @@ bool LLAgent::needsRenderAvatar()
return mShowAvatar && mOutfitChosen;
}
- if (gAgentCamera.cameraMouselook() && !LLVOAvatar::sVisibleInFirstPerson)
+ if (gAgentCamera.isHidingAvatarForFirstPerson() && !LLVOAvatar::sVisibleInFirstPerson)
{
return false;
}
@@ -2150,7 +2184,7 @@ bool LLAgent::needsRenderHead()
{
return mShowAvatar;
}
- return (LLVOAvatar::sVisibleInFirstPerson && LLPipeline::sReflectionRender) || (mShowAvatar && !gAgentCamera.cameraMouselook());
+ return (LLVOAvatar::sVisibleInFirstPerson && LLPipeline::sReflectionRender) || (mShowAvatar && !gAgentCamera.isHidingAvatarForFirstPerson());
}
//-----------------------------------------------------------------------------
@@ -5017,6 +5051,437 @@ void LLAgent::renderAutoPilotTarget()
}
}
+static U64 g_lastUpdateTime { 0 };
+static F32 g_deltaTime { 0.0f };
+static S32 g_lastUpdateFrame { 0 };
+static S32 g_deltaFrame { 0 };
+
+void LLAgent::updateGameControlMode()
+{
+ // Auto-derive the active mode from avatar/camera state. Cursor mode takes
+ // precedence over Flycam: toggling the mouse cursor on while Flycam is engaged
+ // reports CONTROL_MODE_CURSOR (so the left stick drives the on-screen cursor)
+ // while isUsingFlycam() stays true underneath -- LLAgent::updateFlycam() keeps
+ // driving the camera every frame regardless of the reported mode (it is gated
+ // on LLAgentCamera's own flycam state directly, not on AgentControlMode), and
+ // LLGameControl::setFlycamEngaged() below tells the input-mapping lookup to
+ // let Flycam's own axis/button config (Pan/Tilt/Boom/...) keep driving
+ // whatever physical inputs Cursor mode's own mouse-cursor actions don't claim.
+ // This is how a single Cursor mode ends up with two flavors -- "other axes"
+ // control the avatar/camera depending on whether Flycam is concurrently
+ // engaged -- without needing a distinct mode of its own.
+ // Next: mouselook (the camera mode can be entered while sitting too, so it is
+ // checked ahead of Captive), then sitting/controls-taken maps to Captive,
+ // otherwise the normal Avatar mode.
+ LLGameControl::AgentControlMode mode;
+ if (mUsingMouseCursor)
+ {
+ mode = LLGameControl::CONTROL_MODE_CURSOR;
+ }
+ else if (isUsingFlycam())
+ {
+ mode = LLGameControl::CONTROL_MODE_FLYCAM;
+ }
+ else if (gAgentCamera.cameraMouselook())
+ {
+ mode = LLGameControl::CONTROL_MODE_MOUSELOOK;
+ }
+ else if (isSitting())
+ {
+ mode = LLGameControl::CONTROL_MODE_CAPTIVE;
+ }
+ else
+ {
+ mode = LLGameControl::CONTROL_MODE_AVATAR;
+ }
+ LLGameControl::setFlycamEngaged(isUsingFlycam());
+ LLGameControl::setAgentControlMode(mode);
+ gAgentCamera.updateFlycamCursorVisibility(mode);
+}
+
+void LLAgent::applyExternalActions(const LLGameControl::AgentActions& actions)
+{
+ llassert(LLCoros::on_main_thread_main_coro());
+ // Valid whenever game control is driving avatar or flycam: in FlyCam mode this
+ // only services the flycam on/off toggle (movement bits are ignored below).
+ assert(LLGameControl::isEnabled()
+ && (LLGameControl::willControlAvatar() || LLGameControl::willControlFlycam()));
+
+ // Process the non-flag actions first (see AgentActions comment in
+ // llgamecontrol.h). Each bit in mMiscActionBits is already edge-triggered
+ // by LLGameControllerManager::computeAgentActions() (set only on the
+ // frame the bound button transitions from not-pressed to pressed), so no
+ // further debouncing is needed here -- just react to the bit directly.
+ const U32 misc_actions = actions.mMiscActionBits;
+
+ if (misc_actions & LLGameControl::AVATAR_ACTION_TOGGLE_FLY)
+ {
+ setFlying(!getFlying());
+ }
+
+ if (misc_actions & LLGameControl::AVATAR_ACTION_TOGGLE_FLYCAM)
+ {
+ toggleFlycam();
+ }
+
+ if (misc_actions & LLGameControl::AVATAR_ACTION_TOGGLE_SIT)
+ {
+ if (isSitting())
+ {
+ standUp();
+ }
+ else if (LLViewerObject* seat = find_nearby_seat())
+ {
+ handle_object_sit(seat, LLVector3::zero);
+ }
+ else
+ {
+ sitDown();
+ }
+ }
+
+ if (misc_actions & LLGameControl::AVATAR_ACTION_TOGGLE_SPEAK)
+ {
+ LLVoiceClient::getInstance()->toggleUserPTTState();
+ }
+
+ if (misc_actions & LLGameControl::AVATAR_ACTION_TOGGLE_MOUSELOOK)
+ {
+ // Mirrors LLViewMouselook's "View.Mouselook" toggle.
+ if (!gAgentCamera.cameraMouselook())
+ {
+ gAgentCamera.changeCameraToMouselook();
+ }
+ else
+ {
+ gAgentCamera.changeCameraToDefault();
+ }
+ }
+
+ if (misc_actions & LLGameControl::AVATAR_ACTION_TOGGLE_MOUSE_CURSOR)
+ {
+ toggleMouseCursorMode();
+ }
+
+ if (misc_actions & LLGameControl::AVATAR_ACTION_ESCAPE)
+ {
+ resetToAvatarMode();
+ }
+
+ // CONTROL_MODE_CURSOR: drive the actual on-screen cursor from
+ // actions.mMouseCursorDX/DY (the analog deflection of whatever's bound to "Mouse
+ // left/right"/"Mouse up/down") BEFORE the click dispatch below, so a synthesized
+ // click lands wherever the stick just moved the cursor to, in the same frame.
+ if (LLGameControl::getAgentControlMode() == LLGameControl::CONTROL_MODE_CURSOR)
+ {
+ constexpr F32 MOUSE_CURSOR_PIXELS_PER_SEC = 1200.f;
+ U64 mouse_cursor_now = LLFrameTimer::getTotalTime();
+ F32 mouse_cursor_dt = (mLastMouseCursorUpdate == 0)
+ ? 0.f : F32(mouse_cursor_now - mLastMouseCursorUpdate) / (F32)(USEC_PER_SEC);
+ mLastMouseCursorUpdate = mouse_cursor_now;
+
+ if (actions.mMouseCursorDX != 0.f || actions.mMouseCursorDY != 0.f)
+ {
+ LLCoordGL cursor_pos = gViewerWindow->getCurrentMouse();
+ S32 new_x = cursor_pos.mX + ll_round(actions.mMouseCursorDX * MOUSE_CURSOR_PIXELS_PER_SEC * mouse_cursor_dt);
+ // LLCoordGL's Y increases upward, but mMouseCursorDY is in screen-frame
+ // (Y increases downward, same as CURSOR_DY/CURSOR_PY on the wire), so it
+ // subtracts here rather than adds.
+ S32 new_y = cursor_pos.mY - ll_round(actions.mMouseCursorDY * MOUSE_CURSOR_PIXELS_PER_SEC * mouse_cursor_dt);
+ gViewerWindow->moveCursorTo(new_x, new_y);
+ }
+ }
+ else
+ {
+ mLastMouseCursorUpdate = 0;
+ }
+
+ // Simulated mouse buttons (see llgamecontrol.h's AvatarMouseButton) are
+ // level-triggered: actions.mMouseButtonBits reflects which buttons are
+ // currently held, so the press/release edges are found here by diffing
+ // against last frame's value. Each edge is dispatched through
+ // LLViewerWindow exactly like a real mouse click, at the current cursor
+ // position, so it drives hover/pick/pie-menu/tool-manager the same way a
+ // hardware click would (e.g. touching/grabbing objects, walk-to-clicked,
+ // pie menu on right click).
+ U32 mouse_pressed_edges = actions.mMouseButtonBits & ~mPrevMouseButtonBits;
+ U32 mouse_released_edges = ~actions.mMouseButtonBits & mPrevMouseButtonBits;
+ mPrevMouseButtonBits = actions.mMouseButtonBits;
+
+ if (mouse_pressed_edges & LLGameControl::AVATAR_MOUSE_BUTTON_LEFT)
+ {
+ gViewerWindow->handleMouseDown(gViewerWindow->getWindow(), gViewerWindow->getCurrentMouse(), MASK_NONE);
+ }
+ if (mouse_released_edges & LLGameControl::AVATAR_MOUSE_BUTTON_LEFT)
+ {
+ gViewerWindow->handleMouseUp(gViewerWindow->getWindow(), gViewerWindow->getCurrentMouse(), MASK_NONE);
+ }
+ if (mouse_pressed_edges & LLGameControl::AVATAR_MOUSE_BUTTON_RIGHT)
+ {
+ gViewerWindow->handleRightMouseDown(gViewerWindow->getWindow(), gViewerWindow->getCurrentMouse(), MASK_NONE);
+ }
+ if (mouse_released_edges & LLGameControl::AVATAR_MOUSE_BUTTON_RIGHT)
+ {
+ gViewerWindow->handleRightMouseUp(gViewerWindow->getWindow(), gViewerWindow->getCurrentMouse(), MASK_NONE);
+ }
+
+ // actions.mIsRunning is derived purely from how hard the controller's own
+ // movement axes are pushed (see computeAgentActions()'s analog hysteresis).
+ // Only let it drive the agent's running state while the controller is
+ // actually supplying movement this frame -- otherwise an idle-but-connected
+ // controller would fight the keyboard (e.g. clobbering double-tap-to-run)
+ // every frame even though the player is moving with the keyboard instead.
+ constexpr U32 CONTROLLER_MOVEMENT_FLAGS = AGENT_CONTROL_AT_POS | AGENT_CONTROL_AT_NEG
+ | AGENT_CONTROL_LEFT_POS | AGENT_CONTROL_LEFT_NEG;
+ if ((actions.mControlFlags & CONTROLLER_MOVEMENT_FLAGS)
+ && actions.mIsRunning != getRunning())
+ {
+ if (actions.mIsRunning)
+ {
+ setRunning();
+ }
+ else
+ {
+ clearRunning();
+ }
+ sendWalkRun(actions.mIsRunning);
+ }
+
+ mExternalActionFlags = actions.mControlFlags;
+
+ // measure delta time and frame
+ // Note: it is possible for the deltas to be very large
+ // and it is the duty of the code that uses them to clamp as necessary
+ U64 now = LLFrameTimer::getTotalTime();
+ g_deltaTime = F32(now - g_lastUpdateTime) / (F32)(USEC_PER_SEC);
+ g_lastUpdateTime = now;
+
+ S32 frame_count = LLFrameTimer::getFrameCount();
+ g_deltaFrame = frame_count - g_lastUpdateFrame;
+ g_lastUpdateFrame = frame_count;
+
+ if (isUsingFlycam())
+ {
+ // Flycam will be updated later by exernal context
+ return;
+ }
+
+ S32 direction = (S32)(mExternalActionFlags & AGENT_CONTROL_AT_POS)
+ - (S32)((mExternalActionFlags & AGENT_CONTROL_AT_NEG) >> 1);
+ if (direction != 0)
+ {
+ moveAt(direction);
+ }
+
+ static U32 last_non_fly_frame = 0;
+ static U64 last_non_fly_time = 0;
+ direction = (S32)(mExternalActionFlags & AGENT_CONTROL_UP_POS)
+ - (S32)((mExternalActionFlags & AGENT_CONTROL_UP_NEG) >> 1);
+ if (direction != 0)
+ {
+ // HACK: this auto-fly logic based on original code still extant in llviewerinput.cpp::agent_jump()
+ // but has been cleaned up.
+ // TODO?: DRY this logic
+ if (direction > 0)
+ {
+ if (!getFlying()
+ && !upGrabbed()
+ && gSavedSettings.getBOOL("AutomaticFly"))
+ {
+ constexpr F32 FLY_TIME = 0.5f;
+ constexpr U32 FLY_FRAMES = 4;
+ F32 delta_time = (F32)(now - last_non_fly_time) / (F32)(USEC_PER_SEC);
+ U32 delta_frames = frame_count - last_non_fly_frame;
+ if( delta_time > FLY_TIME
+ && delta_frames > FLY_FRAMES)
+ {
+ setFlying(TRUE);
+ }
+ }
+ }
+ else
+ {
+ last_non_fly_frame = frame_count;
+ last_non_fly_time = now;
+ }
+
+ moveUp(direction);
+ }
+ else if (!getFlying())
+ {
+ last_non_fly_frame = frame_count;
+ last_non_fly_time = now;
+ }
+
+ direction = (S32)(mExternalActionFlags & AGENT_CONTROL_LEFT_POS)
+ - (S32)((mExternalActionFlags & AGENT_CONTROL_LEFT_NEG) >> 1);
+ if (direction != 0)
+ {
+ moveLeft(direction);
+ }
+
+ direction = (S32)(mExternalActionFlags & AGENT_CONTROL_YAW_POS)
+ - (S32)((mExternalActionFlags & AGENT_CONTROL_YAW_NEG) >> 1);
+ // The "Slow".."Fast" preferences slider scales Mouselook's turn/look speed
+ // and Avatar's turn speed.
+ F32 turn_speed_factor = 1.0f;
+ F32 look_speed_factor = 1.0f;
+ LLGameControl::AgentControlMode control_mode = LLGameControl::getAgentControlMode();
+ if (control_mode == LLGameControl::CONTROL_MODE_MOUSELOOK)
+ {
+ turn_speed_factor = LLGameControl::getSpeedFactor(LLGameControl::getModeName(control_mode));
+ look_speed_factor = turn_speed_factor;
+ }
+ else if (control_mode == LLGameControl::CONTROL_MODE_AVATAR)
+ {
+ turn_speed_factor = LLGameControl::getSpeedFactor(LLGameControl::getModeName(control_mode));
+ }
+
+ if (direction != 0)
+ {
+ F32 sign = (direction < 0 ? -1.0f : 1.0f);
+ // actions.mYawAmplitude is the analog stick deflection ([-1, 1]) that
+ // set this flag; use its magnitude to modulate turn speed instead of
+ // always snapping to full rate
+ moveYaw(sign * fabs(actions.mYawAmplitude) * turn_speed_factor);
+ }
+
+ {
+ F32 pitch_sign = ((mExternalActionFlags & AGENT_CONTROL_PITCH_POS) > 0 ? 1.0f : 0.0f)
+ - ((mExternalActionFlags & AGENT_CONTROL_PITCH_NEG) > 0 ? 1.0f : 0.0f);
+ // Same analog modulation as yaw above, via actions.mPitchAmplitude.
+ movePitch(pitch_sign * fabs(actions.mPitchAmplitude) * look_speed_factor);
+ }
+
+ // actions.mZoomAmplitude ([-1, 1], from "Zoom +/-"/"Zoom +"/"Zoom -") drives the
+ // camera's field-of-view zoom. Positive amplitude zooms in (narrows the FOV);
+ if (actions.mZoomAmplitude != 0.f)
+ {
+ constexpr F32 FOV_ZOOM_RATE = 1.2f;
+ F32 fov_scale = powf(FOV_ZOOM_RATE, -actions.mZoomAmplitude * g_deltaTime);
+ LLViewerCamera* camera = LLViewerCamera::getInstance();
+ camera->setDefaultFOV(camera->getDefaultFOV() * fov_scale);
+ gSavedSettings.setF32("CameraAngle", camera->getView());
+ }
+
+ // actions.mScrollAmplitude ([-1, 1], from "Scroll +/-"/"Scroll +"/"Scroll -")
+ // moves the camera toward/away from the avatar the same way the mousewheel does,
+ // so the third-person/follow-cam distance persists once the avatar moves (unlike
+ // the keyboard's camera in/out keys, which unlock the view from the avatar).
+ // Positive amplitude scrolls in (camera moves closer); scrollCamera()'s clicks
+ // are positive-out like the mousewheel's.
+ if (actions.mScrollAmplitude != 0.f)
+ {
+ constexpr F32 SCROLL_CLICKS_PER_SECOND = 8.f;
+ gAgentCamera.scrollCamera(-actions.mScrollAmplitude * SCROLL_CLICKS_PER_SECOND * g_deltaTime);
+ }
+
+ if (mExternalActionFlags & AGENT_CONTROL_STOP)
+ {
+ setControlFlags(AGENT_CONTROL_STOP);
+ }
+
+ // HACK: AGENT_CONTROL_NUDGE_AT_POS is used to toggle running
+ if ((mExternalActionFlags & AGENT_CONTROL_NUDGE_AT_POS) > 0)
+ {
+ if (mToggleRun)
+ {
+ if (getRunning())
+ {
+ clearRunning();
+ sendWalkRun(false);
+ }
+ else
+ {
+ setRunning();
+ sendWalkRun(true);
+ }
+ }
+ mToggleRun = false;
+ }
+ else
+ {
+ mToggleRun = true;
+ }
+
+ if ((mExternalActionFlags & AGENT_CONTROL_SIT_ON_GROUND) > 0
+ && isAgentAvatarValid() && !isSitting() && !getFlying() && !gAgentAvatarp->mInAir)
+ {
+ // "Sit": only sit if currently standing; a no-op otherwise
+ // (already sitting, flying, or airborne).
+ sitDown();
+ }
+ else if ((mExternalActionFlags & AGENT_CONTROL_STAND_UP) > 0 && isSitting())
+ {
+ // "Stand up": only stand if currently sitting.
+ standUp();
+ }
+}
+
+bool LLAgent::isUsingFlycam() const
+{
+ return gAgentCamera.isUsingFlycam();
+}
+
+void LLAgent::toggleFlycam()
+{
+ gAgentCamera.toggleFlycam();
+}
+
+void LLAgent::setFlycamKeyInput(U8 channel, F32 value)
+{
+ gAgentCamera.setFlycamKeyInput(channel, value);
+}
+
+void LLAgent::setFlycamKeyUnroll(bool unroll)
+{
+ gAgentCamera.setFlycamKeyUnroll(unroll);
+}
+
+bool LLAgent::isCameraExternallyDriven() const
+{
+ // Both flycam input sources now share one toggle state on LLAgentCamera --
+ // LLViewerJoystick::getOverrideCamera() forwards to the same flag.
+ return gAgentCamera.isUsingFlycam();
+}
+
+void LLAgent::toggleMouseCursorMode()
+{
+ mUsingMouseCursor = !mUsingMouseCursor;
+ mLastMouseCursorUpdate = 0; // avoid a jump from a stale delta-time baseline
+}
+
+void LLAgent::resetToAvatarMode()
+{
+ if (mUsingMouseCursor)
+ {
+ toggleMouseCursorMode();
+ }
+ if (isUsingFlycam())
+ {
+ toggleFlycam();
+ }
+ if (gAgentCamera.cameraMouselook())
+ {
+ gAgentCamera.changeCameraToDefault();
+ }
+}
+
+void LLAgent::pressGameControlButton(U8 button_index)
+{
+ mGameControlButtonsFromKeys |= (1U << button_index);
+}
+
+void LLAgent::releaseGameControlButton(U8 button_index)
+{
+ mGameControlButtonsFromKeys &= ~(1U << button_index);
+}
+
+void LLAgent::updateFlycam()
+{
+ gAgentCamera.updateFlycam(g_deltaTime);
+}
+
/********************************************************************************/
//-----------------------------------------------------------------------------