diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index a02ddccb6bc781a5624319370d6d661ba4c11f04..637326acd0f86439ebb23eeaeed38a2a056985a8 100755 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -212,10 +212,18 @@ void LLAgentCamera::init() mCameraOffsetInitial[CAMERA_PRESET_REAR_VIEW] = gSavedSettings.getControl("CameraOffsetRearView"); mCameraOffsetInitial[CAMERA_PRESET_FRONT_VIEW] = gSavedSettings.getControl("CameraOffsetFrontView"); mCameraOffsetInitial[CAMERA_PRESET_GROUP_VIEW] = gSavedSettings.getControl("CameraOffsetGroupView"); +// [RLVa:KB] - Checked: RLVa-2.0.0 + mCameraOffsetInitial[CAMERA_RLV_SETCAM_VIEW] = gSavedSettings.declareVec3("CameraOffsetRLVaView", LLVector3(mCameraOffsetInitial[CAMERA_PRESET_REAR_VIEW]->getDefault()), "Declared in code", LLControlVariable::PERSIST_NO); + mCameraOffsetInitial[CAMERA_RLV_SETCAM_VIEW]->setHiddenFromSettingsEditor(true); +// [/RLVa:KB] mFocusOffsetInitial[CAMERA_PRESET_REAR_VIEW] = gSavedSettings.getControl("FocusOffsetRearView"); mFocusOffsetInitial[CAMERA_PRESET_FRONT_VIEW] = gSavedSettings.getControl("FocusOffsetFrontView"); mFocusOffsetInitial[CAMERA_PRESET_GROUP_VIEW] = gSavedSettings.getControl("FocusOffsetGroupView"); +// [RLVa:KB] - Checked: RLVa-2.0.0 + mFocusOffsetInitial[CAMERA_RLV_SETCAM_VIEW] = gSavedSettings.declareVec3("FocusOffsetRLVaView", LLVector3(mFocusOffsetInitial[CAMERA_PRESET_REAR_VIEW]->getDefault()), "Declared in code", LLControlVariable::PERSIST_NO); + mFocusOffsetInitial[CAMERA_RLV_SETCAM_VIEW]->setHiddenFromSettingsEditor(true); +// [/RLVa:KB] mCameraCollidePlane.clearVec(); mCurrentCameraDistance = getCameraOffsetInitial().magVec() * gSavedSettings.getF32("CameraOffsetScale"); @@ -1147,7 +1155,7 @@ void LLAgentCamera::updateCamera() // [RLVa:KB] - Checked: RLVa-2.0.0 // Set focus back on our avie if something changed it - if ( (gRlvHandler.hasBehaviour(RLV_BHVR_CAMUNLOCK)) && (cameraThirdPerson()) && (!getFocusOnAvatar()) ) + if ( (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_UNLOCK)) && (cameraThirdPerson()) && (!getFocusOnAvatar()) ) { setFocusOnAvatar(TRUE, FALSE); } @@ -2333,6 +2341,26 @@ void LLAgentCamera::changeCameraToCustomizeAvatar() void LLAgentCamera::switchCameraPreset(ECameraPreset preset) { +// [RLVa:KB] - Checked: RLVa-2.0.0 + if (RlvActions::isRlvEnabled()) + { + // Don't allow changing away from the our view if an object is restricting it + if (RlvActions::isCameraPresetLocked()) + preset = CAMERA_RLV_SETCAM_VIEW; + + // Don't reset anything if our view is already current + if ( (CAMERA_RLV_SETCAM_VIEW == preset) && (CAMERA_RLV_SETCAM_VIEW == mCameraPreset) ) + return; + + // Reset our view when switching away + if (CAMERA_RLV_SETCAM_VIEW != preset) + { + mCameraOffsetInitial[CAMERA_RLV_SETCAM_VIEW]->resetToDefault(); + mFocusOffsetInitial[CAMERA_RLV_SETCAM_VIEW]->resetToDefault(); + } + } +// [/RLVa:KB] + //zoom is supposed to be reset for the front and group views mCameraZoomFraction = 1.f; diff --git a/indra/newview/llagentcamera.h b/indra/newview/llagentcamera.h index ab793ff316b2ea54eef884323d6b81506b66a07b..3a448de7839eab0ff3fdb103b7f989b1adaaf549 100755 --- a/indra/newview/llagentcamera.h +++ b/indra/newview/llagentcamera.h @@ -56,7 +56,12 @@ enum ECameraPreset CAMERA_PRESET_FRONT_VIEW, /** "Above and to the left, over the shoulder, pulled back a little on the zoom" */ - CAMERA_PRESET_GROUP_VIEW + CAMERA_PRESET_GROUP_VIEW, + +// [RLVa:KB] - Checked: RLVa-2.0.0 + /* Used by RLVa */ + CAMERA_RLV_SETCAM_VIEW +// [/RLVa:KB] }; //------------------------------------------------------------------------ diff --git a/indra/newview/llviewercamera.cpp b/indra/newview/llviewercamera.cpp index ac099614021c33e2588bb4798d715d01b8a106a4..314f6417deb53141bffa530753a5c03bc23de695 100755 --- a/indra/newview/llviewercamera.cpp +++ b/indra/newview/llviewercamera.cpp @@ -42,6 +42,7 @@ #include "lltoolmgr.h" #include "llviewerjoystick.h" // [RLVa:KB] - Checked: 2010-04-11 (RLVa-1.2.0e) +#include "rlvactions.h" #include "rlvhandler.h" // [/RLVa:KB] @@ -882,6 +883,12 @@ BOOL LLViewerCamera::areVertsVisible(LLViewerObject* volumep, BOOL all_verts) void LLViewerCamera::setDefaultFOV(F32 vertical_fov_rads) { +// [RLVa:KB] - Checked: RLVa-2.0.0 + F32 nCamFOVMin, nCamFOVMax; + if ( (RlvActions::isRlvEnabled()) && (RlvActions::getCameraFOVLimits(nCamFOVMin, nCamFOVMax)) ) + vertical_fov_rads = llclamp(vertical_fov_rads, nCamFOVMin, nCamFOVMax); +// [/RLVa:KB] + vertical_fov_rads = llclamp(vertical_fov_rads, getMinView(), getMaxView()); setView(vertical_fov_rads); mCameraFOVDefault = vertical_fov_rads; diff --git a/indra/newview/rlvactions.cpp b/indra/newview/rlvactions.cpp index 102729a68bb353a2895a1e531390c29013f083fe..89bba202fdfa29dc8735dfda560ef2d6ecf94a66 100644 --- a/indra/newview/rlvactions.cpp +++ b/indra/newview/rlvactions.cpp @@ -1,27 +1,70 @@ -/** +/** * - * Copyright (c) 2009-2013, Kitty Barnett - * - * The source code in this file is provided to you under the terms of the + * Copyright (c) 2009-2016, Kitty Barnett + * + * The source code in this file is provided to you under the terms of the * GNU Lesser General Public License, version 2.1, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. Terms of the LGPL can be found in doc/LGPL-licence.txt + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. Terms of the LGPL can be found in doc/LGPL-licence.txt * in this distribution, or online at http://www.gnu.org/licenses/lgpl-2.1.txt - * + * * By copying, modifying or distributing this software, you acknowledge that - * you have read and understood your obligations described above, and agree to + * you have read and understood your obligations described above, and agree to * abide by those obligations. - * + * */ #include "llviewerprecompiledheaders.h" #include "llagent.h" #include "llimview.h" +#include "llviewercamera.h" #include "llvoavatarself.h" #include "rlvactions.h" #include "rlvhelper.h" #include "rlvhandler.h" +// ============================================================================ +// Camera +// + +bool RlvActions::canChangeCameraPreset(const LLUUID& idRlvObject) +{ + // NOTE: if an object has exclusive camera controls then all other objects are locked out + return + ( (!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM)) || (gRlvHandler.hasBehaviour(idRlvObject, RLV_BHVR_SETCAM)) ) && + (!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_EYEOFFSET)) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOCUSOFFSET)); +} + +bool RlvActions::canChangeCameraFOV(const LLUUID& idRlvObject) +{ + // NOTE: if an object has exclusive camera controls then all other objects are locked out + return (!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM)) || (gRlvHandler.hasBehaviour(idRlvObject, RLV_BHVR_SETCAM)); +} + +bool RlvActions::isCameraPresetLocked() +{ + return (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_EYEOFFSET)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOCUSOFFSET)); +} + +bool RlvActions::isCameraFOVClamped() +{ + return (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOVMIN)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOVMAX)); +} + +bool RlvActions::getCameraFOVLimits(F32& nFOVMin, F32& nFOVMax) +{ + static RlvCachedBehaviourModifier<float> sCamFovMin(RLV_MODIFIER_SETCAM_FOVMIN); + static RlvCachedBehaviourModifier<float> sCamFovMax(RLV_MODIFIER_SETCAM_FOVMAX); + + bool fClampMax = gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOVMAX); + nFOVMax = (fClampMax) ? sCamFovMax : LLViewerCamera::getInstance()->getMaxView(); + + bool fClampMin = gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOVMIN); + nFOVMin = (fClampMin) ? sCamFovMin : LLViewerCamera::getInstance()->getMinView(); + + return (fClampMin) || (fClampMax); +} + // ============================================================================ // Communication/Avatar interaction // diff --git a/indra/newview/rlvactions.h b/indra/newview/rlvactions.h index 9d616f092556b1759b808ec31083b976fcf8c877..e93060f384584c37d6095ff44995493a550e6107 100644 --- a/indra/newview/rlvactions.h +++ b/indra/newview/rlvactions.h @@ -25,6 +25,35 @@ class RlvActions { + // ====== + // Camera + // ====== +public: + /* + * Returns true if the specified object can manipulate the camera offset and/or focus offset values + */ + static bool canChangeCameraPreset(const LLUUID& idRlvObject); + + /* + * Returns true if the specified object cannot manipulate the camera FOV + */ + static bool canChangeCameraFOV(const LLUUID& idRlvObject); + + /* + * Returns true if the camera offset and focus offset are locked (prevents changing the current camera preset) + */ + static bool isCameraPresetLocked(); + + /* + * Returns true if the camera's FOV is currently restricted/clamped + */ + static bool isCameraFOVClamped(); + + /* + * Retrieves the current camera FOV limits - returns isCameraFOVClamped() + */ + static bool getCameraFOVLimits(F32& nFOVMin, F32& nFOVMax); + // ================================ // Communication/Avatar interaction // ================================ @@ -122,13 +151,13 @@ public: // ================ public: /* - * Convenience function to check for a behaviour without having to include rlvhandler.h. + * Convenience function to check for a behaviour without having to include rlvhandler.h * Do NOT call this function if speed is important (i.e. per-frame) */ static bool hasBehaviour(ERlvBehaviour eBhvr); /* - * Returns true if a - P2P or group - IM session is open with the specified UUID. + * Returns true if a - P2P or group - IM session is open with the specified UUID */ static bool hasOpenP2PSession(const LLUUID& idAgent); static bool hasOpenGroupSession(const LLUUID& idGroup); diff --git a/indra/newview/rlvcommon.h b/indra/newview/rlvcommon.h index 89da91c9d02654c8fbbe6f22d0231a7dd00a2b48..4dde21887db96fc8a34c7cca412eeedf34df9b5c 100644 --- a/indra/newview/rlvcommon.h +++ b/indra/newview/rlvcommon.h @@ -56,7 +56,7 @@ class RlvObject; struct RlvException; typedef boost::variant<std::string, LLUUID, S32, ERlvBehaviour> RlvExceptionOption; -typedef boost::variant<int, float> RlvBehaviourModifierValue; +typedef boost::variant<int, float, LLVector3> RlvBehaviourModifierValue; class RlvGCTimer; diff --git a/indra/newview/rlvdefines.h b/indra/newview/rlvdefines.h index c136c9921d173fac7a403eb613bdf51f6f74da80..7814545bd096ab5bf98acaaaa0ce9a9978566489 100644 --- a/indra/newview/rlvdefines.h +++ b/indra/newview/rlvdefines.h @@ -157,7 +157,6 @@ enum ERlvBehaviour { RLV_BHVR_SETENV, // "setenv" RLV_BHVR_ALWAYSRUN, // "alwaysrun" RLV_BHVR_TEMPRUN, // "temprun" - RLV_BHVR_CAMUNLOCK, RLV_BHVR_DETACHME, // "detachme" RLV_BHVR_ATTACHTHIS, // "attachthis" RLV_BHVR_ATTACHTHISEXCEPT, // "attachthis_except" @@ -165,7 +164,6 @@ enum ERlvBehaviour { RLV_BHVR_DETACHTHISEXCEPT, // "detachthis_except" RLV_BHVR_ADJUSTHEIGHT, // "adjustheight" RLV_BHVR_TPTO, // "tpto" - RLV_BHVR_CAMFOCUS, RLV_BHVR_VERSION, // "version" RLV_BHVR_VERSIONNEW, // "versionnew" RLV_BHVR_VERSIONNUM, // "versionnum" @@ -190,12 +188,27 @@ enum ERlvBehaviour { RLV_BHVR_GETSTATUSALL, // "getstatusall" RLV_CMD_FORCEWEAR, // Internal representation of all force wear commands + // Camera + RLV_BHVR_SETCAM, // Gives an object exclusive control of the user's camera + RLV_BHVR_SETCAM_EYEOFFSET, // Changes the default camera offset + RLV_BHVR_SETCAM_FOCUSOFFSET, // Changes the default camera focus offset + RLV_BHVR_SETCAM_FOCUS, // Forces the camera focus and/or position to a specific object, avatar or position + RLV_BHVR_SETCAM_FOV, // Changes the current (vertical) field of view + RLV_BHVR_SETCAM_FOVMIN, // Enforces a minimum (vertical) FOV + RLV_BHVR_SETCAM_FOVMAX, // Enforces a maximum (vertical) FOV + RLV_BHVR_SETCAM_UNLOCK, // Forces the camera focus to the user's avatar + RLV_BHVR_COUNT, RLV_BHVR_UNKNOWN }; enum ERlvBehaviourModifier { + RLV_MODIFIER_SETCAM_EYEOFFSET, + RLV_MODIFIER_SETCAM_FOCUSOFFSET, + RLV_MODIFIER_SETCAM_FOVMIN, + RLV_MODIFIER_SETCAM_FOVMAX, + RLV_MODIFIER_COUNT, RLV_MODIFIER_UNKNOWN }; diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp index be4bdb39c2cf27391aac8f45ff363cdfe87521ef..5309c8d79b0980d1fdf76313d5fd325f80c0962b 100644 --- a/indra/newview/rlvhandler.cpp +++ b/indra/newview/rlvhandler.cpp @@ -30,7 +30,7 @@ #include "llviewerregion.h" // Command specific includes -#include "llagentcamera.h" // @camfocus +#include "llagentcamera.h" // @setcam and related #include "llenvmanager.h" // @setenv #include "lloutfitslist.h" // @showinv - "Appearance / My Outfits" panel #include "llpaneloutfitsinventory.h" // @showinv - "Appearance" floater @@ -38,9 +38,10 @@ #include "llsidepanelappearance.h" // @showinv - "Appearance / Edit appearance" panel #include "lltabcontainer.h" // @showinv - Tab container control for inventory tabs #include "lltoolmgr.h" // @edit -#include "llviewercamera.h" // @camfocus +#include "llviewercamera.h" // @setcam and related // RLVa includes +#include "rlvactions.h" #include "rlvfloaters.h" #include "rlvhandler.h" #include "rlvhelper.h" @@ -138,6 +139,23 @@ RlvHandler::~RlvHandler() // Behaviour related functions // +bool RlvHandler::findBehaviour(ERlvBehaviour eBhvr, std::list<const RlvObject*>& lObjects) const +{ + lObjects.clear(); + for (const auto& objEntry : m_Objects) + if (objEntry.second.hasBehaviour(eBhvr, false)) + lObjects.push_back(&objEntry.second); + return !lObjects.empty(); +} + +bool RlvHandler::hasBehaviour(const LLUUID& idRlvObj, ERlvBehaviour eBhvr, const std::string& strOption) const +{ + rlv_object_map_t::const_iterator itObj = m_Objects.find(idRlvObj); + if (m_Objects.end() != itObj) + return itObj->second.hasBehaviour(eBhvr, strOption, false); + return false; +} + bool RlvHandler::hasBehaviourExcept(ERlvBehaviour eBhvr, const std::string& strOption, const LLUUID& idObj) const { for (rlv_object_map_t::const_iterator itObj = m_Objects.begin(); itObj != m_Objects.end(); ++itObj) @@ -296,7 +314,7 @@ ERlvCmdRet RlvHandler::processCommand(const RlvCommand& rlvCmd, bool fFromObj) case RLV_TYPE_ADD: // Checked: 2009-11-26 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f { if ( (m_Behaviours[rlvCmd.getBehaviourType()]) && - ( (RLV_BHVR_SETDEBUG == rlvCmd.getBehaviourType()) || (RLV_BHVR_SETENV == rlvCmd.getBehaviourType()) ) ) + ( (RLV_BHVR_SETCAM == rlvCmd.getBehaviourType()) || (RLV_BHVR_SETDEBUG == rlvCmd.getBehaviourType()) || (RLV_BHVR_SETENV == rlvCmd.getBehaviourType()) ) ) { // Some restrictions can only be held by one single object to avoid deadlocks RLV_DEBUGS << "\t- " << rlvCmd.getBehaviour() << " is already set by another object => discarding" << RLV_ENDL; @@ -466,70 +484,6 @@ bool RlvHandler::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& return false; } -// Handles: @camfocus:<uuid>[;<dist>[;<direction>]]=force -template<> template<> -ERlvCmdRet RlvForceHandler<RLV_BHVR_CAMFOCUS>::onCommand(const RlvCommand& rlvCmd) -{ - std::vector<std::string> optionList; - if (!RlvCommandOptionHelper::parseStringList(rlvCmd.getOption(), optionList)) - return RLV_RET_FAILED_OPTION; - - LLVector3 posAgent; - LLVector3d posGlobal; - F32 camDistance; - - // Get the focus position/object (and verify it is known) - LLUUID idObject; LLVector3 posRegion; - if (RlvCommandOptionHelper::parseOption(optionList[0], idObject)) - { - const LLViewerObject* pObj = gObjectList.findObject(idObject); - if (!pObj) - return RLV_RET_FAILED_OPTION; - posAgent = pObj->getPositionAgent(); - posGlobal = pObj->getPositionGlobal(); - camDistance = pObj->getScale().magVec(); - } - else if (RlvCommandOptionHelper::parseOption(optionList[0], posRegion)) - { - const LLViewerRegion* pRegion = gAgent.getRegion(); - if (!pRegion) - return RLV_RET_FAILED_UNKNOWN; - posAgent = pRegion->getPosAgentFromRegion(posRegion); - posGlobal = pRegion->getPosGlobalFromRegion(posRegion); - camDistance = 0.0f; - } - else - { - return RLV_RET_FAILED_OPTION; - } - - // Get the camera distance - if ( (optionList.size() > 1) && (!optionList[1].empty()) ) - { - if (!RlvCommandOptionHelper::parseOption(optionList[1], camDistance)) - return RLV_RET_FAILED_OPTION; - } - - // Get the directional vector (or calculate it from the current camera position) - LLVector3 camDirection; - if ( (optionList.size() > 2) && (!optionList[2].empty()) ) - { - if (!RlvCommandOptionHelper::parseOption(optionList[2], camDirection)) - return RLV_RET_FAILED_OPTION; - } - else - { - camDirection = LLViewerCamera::getInstance()->getOrigin() - posAgent; - } - camDirection.normVec(); - - // Move the camera in place - gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); - gAgentCamera.setCameraPosAndFocusGlobal(posGlobal + LLVector3d(camDirection * llmax(F_APPROXIMATELY_ZERO, camDistance)), posGlobal, idObject); - - return RLV_RET_SUCCESS; -} - // Checked: 2010-08-29 (RLVa-1.2.1c) | Modified: RLVa-1.2.1c void RlvHandler::onSitOrStand(bool fSitting) { @@ -1436,10 +1390,19 @@ ERlvCmdRet RlvBehaviourGenericHandler<RLV_OPTION_MODIFIER>::onCommand(const RlvC if ( (!rlvCmd.hasOption()) || (!pBhvrModifier) || (!pBhvrModifier->convertOptionValue(rlvCmd.getOption(), modValue)) ) return RLV_RET_FAILED_OPTION; + // HACK-RLVa: reference counting doesn't happen until control returns to our caller but the modifier callbacks will happen now so we need to adjust the reference counts here if (RLV_TYPE_ADD == rlvCmd.getParamType()) + { + gRlvHandler.m_Behaviours[rlvCmd.getBehaviourType()]++; pBhvrModifier->addValue(modValue, rlvCmd.getObjectID()); + gRlvHandler.m_Behaviours[rlvCmd.getBehaviourType()]--; + } else + { + gRlvHandler.m_Behaviours[rlvCmd.getBehaviourType()]--; pBhvrModifier->removeValue(modValue, rlvCmd.getObjectID()); + gRlvHandler.m_Behaviours[rlvCmd.getBehaviourType()]++; + } fRefCount = true; return RLV_RET_SUCCESS; @@ -1499,16 +1462,6 @@ ERlvCmdRet RlvBehaviourAddRemAttachHandler::onCommand(const RlvCommand& rlvCmd, return RLV_RET_SUCCESS; } -// Handles: @sendim=n|y toggles -template<> template<> -void RlvBehaviourHandler<RLV_BHVR_CAMUNLOCK>::onCommandToggle(ERlvBehaviour eBhvr, bool fHasBhvr) -{ - if (fHasBhvr) - { - handle_reset_view(); - } -} - // Handles: @detach[:<attachpt>]=n|y template<> template<> ERlvCmdRet RlvBehaviourHandler<RLV_BHVR_DETACH>::onCommand(const RlvCommand& rlvCmd, bool& fRefCount) @@ -1676,7 +1629,114 @@ void RlvBehaviourToggleHandler<RLV_BHVR_SENDIM>::onCommandToggle(ERlvBehaviour e gSavedPerAccountSettings.getControl("DoNotDisturbModeResponse")->setHiddenFromSettingsEditor(fHasBhvr); } -// Handles: @edit=n|y toggles +// Handles: @setcam_unlock=n|y toggles +template<> template<> +void RlvBehaviourToggleHandler<RLV_BHVR_SETCAM_UNLOCK>::onCommandToggle(ERlvBehaviour eBhvr, bool fHasBhvr) +{ + if (fHasBhvr) + handle_reset_view(); +} + +// Handles: @setcam_eyeoffset:<vector3>=n|y and @setcam_focusoffset:<vector3>=n|y toggles +template<> template<> +void RlvBehaviourCamEyeFocusOffsetHandler::onCommandToggle(ERlvBehaviour eBhvr, bool fHasBhvr) +{ + if (fHasBhvr) + { + gAgentCamera.switchCameraPreset(CAMERA_RLV_SETCAM_VIEW); + } + else + { + const RlvBehaviourModifier* pBhvrEyeModifier = RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_EYEOFFSET); + const RlvBehaviourModifier* pBhvrOffsetModifier = RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_FOCUSOFFSET); + if ( (!pBhvrEyeModifier->hasValue()) && (!pBhvrOffsetModifier->hasValue()) ) + gAgentCamera.switchCameraPreset(CAMERA_PRESET_REAR_VIEW); + } +} + +// Handles: @setcam_eyeoffset:<vector3>=n|y changes +template<> +void RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_EYEOFFSET>::onValueChange() const +{ + if (RlvBehaviourModifier* pBhvrModifier = RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_EYEOFFSET)) + { + LLControlVariable* pControl = gSavedSettings.getControl("CameraOffsetRLVaView"); + if (pBhvrModifier->hasValue()) + pControl->setValue(pBhvrModifier->getValue<LLVector3>().getValue()); + else + pControl->resetToDefault(); + } +} + +// Handles: @setcam_focusoffset:<vector3>=n|y changes +template<> +void RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_FOCUSOFFSET>::onValueChange() const +{ + if (RlvBehaviourModifier* pBhvrModifier = RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_FOCUSOFFSET)) + { + LLControlVariable* pControl = gSavedSettings.getControl("FocusOffsetRLVaView"); + if (pBhvrModifier->hasValue()) + pControl->setValue(pBhvrModifier->getValue<LLVector3>().getValue()); + else + pControl->resetToDefault(); + } +} + +// Handles: @setcam_fovmin:<angle>=n|y changes +template<> +void RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_FOVMIN>::onValueChange() const +{ + LLViewerCamera::instance().setDefaultFOV(LLViewerCamera::instance().getDefaultFOV()); +} + +// Handles: @setcam_fovmax:<angle>=n|y changes +template<> +void RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_FOVMAX>::onValueChange() const +{ + LLViewerCamera::instance().setDefaultFOV(LLViewerCamera::instance().getDefaultFOV()); +} + +// Handles: @setcam=n|y toggles +template<> template<> +void RlvBehaviourToggleHandler<RLV_BHVR_SETCAM>::onCommandToggle(ERlvBehaviour eBhvr, bool fHasBhvr) +{ + // Once an object has exclusive control over the camera only its behaviours should be active. This affects: + // - RLV_BHVR_SETCAM_EYEOFFSET => behaviour modifiers handle this for us + // - RLV_BHVR_SETCAM_FOCUSOFFSET => behaviour modifiers handle this for us + // - RLV_BHVR_SETCAM_FOVMIN => behaviour modifiers handle this for us + // - RLV_BHVR_SETCAM_FOVMAX => behaviour modifiers handle this for us + // - RLV_BHVR_SETCAM_UNLOCK => manually (re)set the reference count (and possibly invoke the toggle handler) + + LLUUID idRlvObject; bool fHasCamUnlock = gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_UNLOCK); + if (fHasBhvr) + { + // Get the UUID of the primary object + std::list<const RlvObject*> lObjects; + gRlvHandler.findBehaviour(RLV_BHVR_SETCAM, lObjects); + idRlvObject = lObjects.front()->getObjectID(); + // Reset the @setcam_unlock reference count + gRlvHandler.m_Behaviours[RLV_BHVR_SETCAM_UNLOCK] = (lObjects.front()->hasBehaviour(RLV_BHVR_SETCAM_UNLOCK, false)) ? 1 : 0; + } + else + { + std::list<const RlvObject*> lObjects; + // Restore the @setcam_unlock reference count + gRlvHandler.findBehaviour(RLV_BHVR_SETCAM_UNLOCK, lObjects); + gRlvHandler.m_Behaviours[RLV_BHVR_SETCAM_UNLOCK] = lObjects.size(); + } + + // Manually invoke the @setcam_unlock toggle handler if we toggled it on/off + if (fHasCamUnlock != gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_UNLOCK)) + RlvBehaviourToggleHandler<RLV_BHVR_SETCAM_UNLOCK>::onCommandToggle(RLV_BHVR_SETCAM_UNLOCK, !fHasCamUnlock); + + gAgentCamera.switchCameraPreset( (fHasBhvr) ? CAMERA_RLV_SETCAM_VIEW : CAMERA_PRESET_REAR_VIEW ); + RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_EYEOFFSET)->setPrimaryObject(idRlvObject); + RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_FOCUSOFFSET)->setPrimaryObject(idRlvObject); + RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_FOVMIN)->setPrimaryObject(idRlvObject); + RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_FOVMAX)->setPrimaryObject(idRlvObject); +} + +// Handles: @setdebug=n|y toggles template<> template<> void RlvBehaviourToggleHandler<RLV_BHVR_SETDEBUG>::onCommandToggle(ERlvBehaviour eBhvr, bool fHasBhvr) { @@ -1954,6 +2014,124 @@ ERlvCmdRet RlvForceHandler<RLV_BHVR_REMOUTFIT>::onCommand(const RlvCommand& rlvC return RLV_RET_SUCCESS; } +// Handles: @setcam_eyeoffset[:<vector3>]=force and @setcam_focusoffset[:<vector3>]=force +template<> template<> +ERlvCmdRet RlvForceCamEyeFocusOffsetHandler::onCommand(const RlvCommand& rlvCmd) +{ + // Enforce exclusive camera locks + if (!RlvActions::canChangeCameraPreset(rlvCmd.getObjectID())) + return RLV_RET_FAILED_LOCK; + + LLControlVariable* pOffsetControl = gSavedSettings.getControl("CameraOffsetRLVaView"); + LLControlVariable* pFocusControl = gSavedSettings.getControl("FocusOffsetRLVaView"); + LLControlVariable* pControl = (rlvCmd.getBehaviourType() == RLV_BHVR_SETCAM_EYEOFFSET) ? pOffsetControl : pFocusControl; + if (rlvCmd.hasOption()) + { + LLVector3 vecOffset; + if (!RlvCommandOptionHelper::parseOption(rlvCmd.getOption(), vecOffset)) + return RLV_RET_FAILED_OPTION; + pControl->setValue(vecOffset.getValue()); + } + else + { + pControl->resetToDefault(); + } + + gAgentCamera.switchCameraPreset( ((pOffsetControl->isDefault()) && (pFocusControl->isDefault())) ? CAMERA_PRESET_REAR_VIEW : CAMERA_RLV_SETCAM_VIEW); + return RLV_RET_SUCCESS; +} + +// Handles: @setcam_focus:<uuid>[;<dist>[;<direction>]]=force +template<> template<> +ERlvCmdRet RlvForceHandler<RLV_BHVR_SETCAM_FOCUS>::onCommand(const RlvCommand& rlvCmd) +{ + std::vector<std::string> optionList; + if (!RlvCommandOptionHelper::parseStringList(rlvCmd.getOption(), optionList)) + return RLV_RET_FAILED_OPTION; + + LLVector3 posAgent; + LLVector3d posGlobal; + F32 camDistance; + + // Get the focus position/object (and verify it is known) + LLUUID idObject; LLVector3 posRegion; + if (RlvCommandOptionHelper::parseOption(optionList[0], idObject)) + { + const LLViewerObject* pObj = gObjectList.findObject(idObject); + if (!pObj) + return RLV_RET_FAILED_OPTION; + if (!pObj->isAvatar()) + { + posAgent = pObj->getPositionAgent(); + posGlobal = pObj->getPositionGlobal(); + } + else + { + /*const*/ LLVOAvatar* pAvatar = (/*const*/ LLVOAvatar*)pObj; + if (pAvatar->mHeadp) + { + posAgent = pAvatar->mHeadp->getWorldPosition(); + posGlobal = pAvatar->getPosGlobalFromAgent(posAgent); + } + } + camDistance = pObj->getScale().magVec(); + } + else if (RlvCommandOptionHelper::parseOption(optionList[0], posRegion)) + { + const LLViewerRegion* pRegion = gAgent.getRegion(); + if (!pRegion) + return RLV_RET_FAILED_UNKNOWN; + posAgent = pRegion->getPosAgentFromRegion(posRegion); + posGlobal = pRegion->getPosGlobalFromRegion(posRegion); + camDistance = 0.0f; + } + else + { + return RLV_RET_FAILED_OPTION; + } + + // Get the camera distance + if ( (optionList.size() > 1) && (!optionList[1].empty()) ) + { + if (!RlvCommandOptionHelper::parseOption(optionList[1], camDistance)) + return RLV_RET_FAILED_OPTION; + } + + // Get the directional vector (or calculate it from the current camera position) + LLVector3 camDirection; + if ( (optionList.size() > 2) && (!optionList[2].empty()) ) + { + if (!RlvCommandOptionHelper::parseOption(optionList[2], camDirection)) + return RLV_RET_FAILED_OPTION; + } + else + { + camDirection = LLViewerCamera::getInstance()->getOrigin() - posAgent; + } + camDirection.normVec(); + + // Move the camera in place + gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); + gAgentCamera.setCameraPosAndFocusGlobal(posGlobal + LLVector3d(camDirection * llmax(F_APPROXIMATELY_ZERO, camDistance)), posGlobal, idObject); + + return RLV_RET_SUCCESS; +} + +// Handles: @setcam_fov[:<angle>]=force +template<> template<> +ERlvCmdRet RlvForceHandler<RLV_BHVR_SETCAM_FOV>::onCommand(const RlvCommand& rlvCmd) +{ + if (!RlvActions::canChangeCameraFOV(rlvCmd.getObjectID())) + return RLV_RET_FAILED_LOCK; + + F32 nFOV = DEFAULT_FIELD_OF_VIEW; + if ( (rlvCmd.hasOption()) && (!RlvCommandOptionHelper::parseOption(rlvCmd.getOption(), nFOV)) ) + return RLV_RET_FAILED_OPTION; + + LLViewerCamera::getInstance()->setDefaultFOV(nFOV); + return RLV_RET_SUCCESS; +} + // Checked: 2010-08-30 (RLVa-1.2.1c) | Modified: RLVa-1.2.1c ERlvCmdRet RlvHandler::onForceWear(const LLViewerInventoryCategory* pFolder, U32 nFlags) const { diff --git a/indra/newview/rlvhandler.h b/indra/newview/rlvhandler.h index c389d9ca3fcfbefa054f1b535413e8caee483210..35d9160b5d99a9f7582089e9b86446018436d654 100644 --- a/indra/newview/rlvhandler.h +++ b/indra/newview/rlvhandler.h @@ -44,9 +44,13 @@ public: // - to check @remoutfit=n -> (see RlvWearableLocks) // - to check exceptions -> isException() public: + // Returns a list of all objects containing the specified behaviour + bool findBehaviour(ERlvBehaviour eBhvr, std::list<const RlvObject*>& lObjects) const; // Returns TRUE is at least one object contains the specified behaviour (and optional option) bool hasBehaviour(ERlvBehaviour eBhvr) const { return (eBhvr < RLV_BHVR_COUNT) ? (0 != m_Behaviours[eBhvr]) : false; } bool hasBehaviour(ERlvBehaviour eBhvr, const std::string& strOption) const; + // Returns TRUE if the specified object contains the specified behaviour (and optional option) + bool hasBehaviour(const LLUUID& idObj, ERlvBehaviour eBhvr, const std::string& strOption = LLStringUtil::null) const; // Returns TRUE if at least one object (except the specified one) contains the specified behaviour (and optional option) bool hasBehaviourExcept(ERlvBehaviour eBhvr, const LLUUID& idObj) const; bool hasBehaviourExcept(ERlvBehaviour eBhvr, const std::string& strOption, const LLUUID& idObj) const; @@ -207,6 +211,7 @@ protected: friend class RlvSharedRootFetcher; // Fetcher needs access to m_fFetchComplete friend class RlvGCTimer; // Timer clear its own point at destruction + template<ERlvBehaviourOptionType optionType> friend struct RlvBehaviourGenericHandler; template<ERlvParamType> friend struct RlvCommandHandlerBaseImpl; template<ERlvParamType, ERlvBehaviour> friend struct RlvCommandHandler; diff --git a/indra/newview/rlvhelper.cpp b/indra/newview/rlvhelper.cpp index 3c9ee3408bf101173f83662d809d98d9a9c0c8e2..6237653cbeb21462535972dff67860648683fb6b 100644 --- a/indra/newview/rlvhelper.cpp +++ b/indra/newview/rlvhelper.cpp @@ -32,9 +32,6 @@ // RlvBehaviourDictionary // -static RlvBehaviourModifier_CompMin s_RlvBehaviourModifier_CompMin; -static RlvBehaviourModifier_CompMax s_RlvBehaviourModifier_CompMax; - /* * Processing of RLVa commands used to be a big switch/case loop with one function for each command type(addrem, reply * and force). This is slowly being replaced with templated command handling which might be more confusing intially @@ -91,7 +88,6 @@ RlvBehaviourDictionary::RlvBehaviourDictionary() addEntry(new RlvBehaviourInfo("attachallthis", RLV_BHVR_ATTACHTHIS, RLV_TYPE_ADDREM, RlvBehaviourInfo::FORCEWEAR_SUBTREE)); addEntry(new RlvBehaviourInfo("attachthis_except", RLV_BHVR_ATTACHTHISEXCEPT, RLV_TYPE_ADDREM, RlvBehaviourInfo::FORCEWEAR_NODE)); addEntry(new RlvBehaviourInfo("attachallthis_except", RLV_BHVR_ATTACHTHISEXCEPT, RLV_TYPE_ADDREM, RlvBehaviourInfo::FORCEWEAR_SUBTREE)); - addEntry(new RlvBehaviourToggleProcessor<RLV_BHVR_CAMUNLOCK, RLV_OPTION_NONE>("camunlock")); addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_NONE>("chatwhisper", RLV_BHVR_CHATWHISPER)); addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_NONE>("chatnormal", RLV_BHVR_CHATNORMAL)); addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_NONE>("chatshout", RLV_BHVR_CHATSHOUT)); @@ -163,6 +159,17 @@ RlvBehaviourDictionary::RlvBehaviourDictionary() addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_NONE>("viewnote", RLV_BHVR_VIEWNOTE)); addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_NONE>("viewscript", RLV_BHVR_VIEWSCRIPT)); addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_NONE>("viewtexture", RLV_BHVR_VIEWTEXTURE)); + // Camera + addEntry(new RlvBehaviourToggleProcessor<RLV_BHVR_SETCAM, RLV_OPTION_NONE>("setcam")); + addEntry(new RlvBehaviourToggleProcessor<RLV_BHVR_SETCAM_EYEOFFSET, RLV_OPTION_MODIFIER, RlvBehaviourCamEyeFocusOffsetHandler>("setcam_eyeoffset", RlvBehaviourInfo::BHVR_EXPERIMENTAL)); + addModifier(RLV_BHVR_SETCAM_EYEOFFSET, RLV_MODIFIER_SETCAM_EYEOFFSET, new RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_EYEOFFSET>(LLVector3::zero, true, nullptr)); + addEntry(new RlvBehaviourToggleProcessor<RLV_BHVR_SETCAM_FOCUSOFFSET, RLV_OPTION_MODIFIER, RlvBehaviourCamEyeFocusOffsetHandler>("setcam_focusoffset", RlvBehaviourInfo::BHVR_EXPERIMENTAL)); + addModifier(RLV_BHVR_SETCAM_FOCUSOFFSET, RLV_MODIFIER_SETCAM_FOCUSOFFSET, new RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_FOCUSOFFSET>(LLVector3::zero, true, nullptr)); + addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_MODIFIER>("setcam_fovmin", RLV_BHVR_SETCAM_FOVMIN)); + addModifier(RLV_BHVR_SETCAM_FOVMIN, RLV_MODIFIER_SETCAM_FOVMIN, new RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_FOVMIN>(DEFAULT_FIELD_OF_VIEW, true, new RlvBehaviourModifier_CompMax())); + addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_MODIFIER>("setcam_fovmax", RLV_BHVR_SETCAM_FOVMAX)); + addModifier(RLV_BHVR_SETCAM_FOVMAX, RLV_MODIFIER_SETCAM_FOVMAX, new RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_FOVMAX>(DEFAULT_FIELD_OF_VIEW, true, new RlvBehaviourModifier_CompMin())); + addEntry(new RlvBehaviourToggleProcessor<RLV_BHVR_SETCAM_UNLOCK, RLV_OPTION_NONE>("setcam_unlock")); // // Force-wear @@ -200,8 +207,11 @@ RlvBehaviourDictionary::RlvBehaviourDictionary() // Force-only // addEntry(new RlvBehaviourInfo("adjustheight", RLV_BHVR_ADJUSTHEIGHT, RLV_TYPE_FORCE)); - addEntry(new RlvForceProcessor<RLV_BHVR_CAMFOCUS>("camfocus", RlvBehaviourInfo::BHVR_EXPERIMENTAL)); addEntry(new RlvForceProcessor<RLV_BHVR_DETACHME>("detachme")); + addEntry(new RlvForceProcessor<RLV_BHVR_SETCAM_FOCUS>("setcam_focus", RlvBehaviourInfo::BHVR_EXPERIMENTAL)); + addEntry(new RlvForceProcessor<RLV_BHVR_SETCAM_EYEOFFSET, RlvForceCamEyeFocusOffsetHandler>("setcam_eyeoffset", RlvBehaviourInfo::BHVR_EXPERIMENTAL)); + addEntry(new RlvForceProcessor<RLV_BHVR_SETCAM_FOCUSOFFSET, RlvForceCamEyeFocusOffsetHandler>("setcam_focusoffset", RlvBehaviourInfo::BHVR_EXPERIMENTAL)); + addEntry(new RlvForceProcessor<RLV_BHVR_SETCAM_FOV>("setcam_fov", RlvBehaviourInfo::BHVR_EXPERIMENTAL)); addEntry(new RlvForceProcessor<RLV_BHVR_SETGROUP>("setgroup")); addEntry(new RlvForceProcessor<RLV_BHVR_SIT>("sit")); addEntry(new RlvForceProcessor<RLV_BHVR_TPTO>("tpto")); @@ -368,8 +378,18 @@ void RlvBehaviourDictionary::toggleBehaviourFlag(const std::string& strBhvr, ERl // RlvBehaviourModifier::RlvBehaviourModifier(const RlvBehaviourModifierValue& defaultValue, bool fAddDefaultOnEmpty, RlvBehaviourModifier_Comp* pValueComparator) - : m_DefaultValue(defaultValue), m_fAddDefaultOnEmpty(fAddDefaultOnEmpty), m_pValueComparator(pValueComparator) + : m_DefaultValue(defaultValue), m_fAddDefaultOnEmpty(fAddDefaultOnEmpty) +{ + m_pValueComparator = (pValueComparator) ? pValueComparator : new RlvBehaviourModifier_Comp(); +} + +RlvBehaviourModifier::~RlvBehaviourModifier() { + if (m_pValueComparator) + { + delete m_pValueComparator; + m_pValueComparator = NULL; + } } bool RlvBehaviourModifier::addValue(const RlvBehaviourModifierValue& modValue, const LLUUID& idObject) @@ -377,13 +397,21 @@ bool RlvBehaviourModifier::addValue(const RlvBehaviourModifierValue& modValue, c if (modValue.which() == m_DefaultValue.which()) { m_Values.insert((m_pValueComparator) ? std::lower_bound(m_Values.begin(), m_Values.end(), std::make_pair(modValue, idObject), boost::bind(&RlvBehaviourModifier_Comp::operator(), m_pValueComparator, _1, _2)) : m_Values.end(), std::make_pair(modValue, idObject)); - onValueChange(); + // NOTE: change signal needs to trigger before modifier handlers so cached values have a chance to update properly m_ChangeSignal(getValue()); + onValueChange(); return true; } return false; } +bool RlvBehaviourModifier::hasValue() const { + // If no primary object is set this returns "any value set"; otherwise it returns "any value set by the primary object" + if ( (!m_pValueComparator) || (m_pValueComparator->m_idPrimaryObject.isNull()) ) + return !m_Values.empty(); + return (!m_Values.empty()) ? m_Values.front().second == m_pValueComparator->m_idPrimaryObject : false; +} + void RlvBehaviourModifier::removeValue(const RlvBehaviourModifierValue& modValue, const LLUUID& idObject) { if ( (modValue.which() == m_DefaultValue.which()) ) @@ -423,6 +451,15 @@ bool RlvBehaviourModifier::convertOptionValue(const std::string& optionValue, Rl modValue = std::stoi(optionValue); return true; } + else if (typeid(LLVector3) == m_DefaultValue.type()) + { + LLVector3 vecOption; + if (3 == sscanf(optionValue.c_str(), "%f/%f/%f", vecOption.mV + 0, vecOption.mV + 1, vecOption.mV + 2)) + { + modValue = vecOption; + return true; + } + } return false; } catch (const std::invalid_argument&) diff --git a/indra/newview/rlvhelper.h b/indra/newview/rlvhelper.h index 50b2850776afd1282ed6a46a652e70a2bbc1aaf9..f8e94468dd80f3880cdde7bcb2f7fa3520805004 100644 --- a/indra/newview/rlvhelper.h +++ b/indra/newview/rlvhelper.h @@ -114,8 +114,10 @@ template<ERlvBehaviour eBhvr> using RlvForceHandler = RlvCommandHandler<RLV_TYPE template<ERlvBehaviour eBhvr> using RlvReplyHandler = RlvCommandHandler<RLV_TYPE_REPLY, eBhvr>; // List of shared handlers -typedef RlvBehaviourHandler<RLV_BHVR_REMATTACH> RlvBehaviourAddRemAttachHandler; // Shared between @addattach and @remattach -typedef RlvForceHandler<RLV_BHVR_REMATTACH> RlvForceRemAttachHandler; // Shared between @remattach and @detach +typedef RlvBehaviourToggleHandler<RLV_BHVR_SETCAM_EYEOFFSET> RlvBehaviourCamEyeFocusOffsetHandler; // Shared between @setcam_eyeoffset and @setcam_focusoffset +typedef RlvBehaviourHandler<RLV_BHVR_REMATTACH> RlvBehaviourAddRemAttachHandler; // Shared between @addattach and @remattach +typedef RlvForceHandler<RLV_BHVR_REMATTACH> RlvForceRemAttachHandler; // Shared between @remattach and @detach +typedef RlvForceHandler<RLV_BHVR_SETCAM_EYEOFFSET> RlvForceCamEyeFocusOffsetHandler; // Shared between @setcam_eyeoffset and @setcam_focusoffset // // RlvCommandProcessor - Templated glue class that brings RlvBehaviourInfo, RlvCommandHandlerBaseImpl and RlvCommandHandler together @@ -197,8 +199,8 @@ struct RlvBehaviourModifier_CompMax : public RlvBehaviourModifier_Comp class RlvBehaviourModifier { public: - RlvBehaviourModifier(const RlvBehaviourModifierValue& defaultValue, bool fAddDefaultOnEmpty, RlvBehaviourModifier_Comp* pValueComparator); - virtual ~RlvBehaviourModifier() {} + RlvBehaviourModifier(const RlvBehaviourModifierValue& defaultValue, bool fAddDefaultOnEmpty, RlvBehaviourModifier_Comp* pValueComparator = nullptr); + virtual ~RlvBehaviourModifier(); /* * Member functions @@ -206,14 +208,15 @@ public: protected: virtual void onValueChange() const {} public: - bool addValue(const RlvBehaviourModifierValue& modValue, const LLUUID& idObject); - bool convertOptionValue(const std::string& optionValue, RlvBehaviourModifierValue& modValue) const; - bool getAddDefault() const { return m_fAddDefaultOnEmpty; } + bool addValue(const RlvBehaviourModifierValue& modValue, const LLUUID& idObject); + bool convertOptionValue(const std::string& optionValue, RlvBehaviourModifierValue& modValue) const; + bool getAddDefault() const { return m_fAddDefaultOnEmpty; } const RlvBehaviourModifierValue& getDefaultValue() const { return m_DefaultValue; } - const RlvBehaviourModifierValue& getValue() const { return (!m_Values.empty()) ? m_Values.front().first : m_DefaultValue; } + const RlvBehaviourModifierValue& getValue() const { return (hasValue()) ? m_Values.front().first : m_DefaultValue; } template<typename T> const T& getValue() const { return boost::get<T>(getValue()); } - void removeValue(const RlvBehaviourModifierValue& modValue, const LLUUID& idObject); - void setPrimaryObject(const LLUUID& idPrimaryObject); + bool hasValue() const; + void removeValue(const RlvBehaviourModifierValue& modValue, const LLUUID& idObject); + void setPrimaryObject(const LLUUID& idPrimaryObject); typedef boost::signals2::signal<void(const RlvBehaviourModifierValue& newValue)> change_signal_t; change_signal_t& getSignal() { return m_ChangeSignal; }