From 54f218f68aa8b083cd170e79efba48775cb94046 Mon Sep 17 00:00:00 2001 From: ZiRee Date: Wed, 20 Apr 2011 05:41:33 +0200 Subject: [PATCH] First draft for a viewer side animation overrider. Still very rough code but almost feature-complete. --- indra/newview/CMakeLists.txt | 6 + indra/newview/ao.cpp | 487 ++++++ indra/newview/ao.h | 112 ++ indra/newview/aoengine.cpp | 1482 +++++++++++++++++ indra/newview/aoengine.h | 188 +++ indra/newview/aoset.cpp | 263 +++ indra/newview/aoset.h | 134 ++ indra/newview/llagentcamera.cpp | 4 + indra/newview/llappviewer.cpp | 6 +- indra/newview/llbottomtray.cpp | 14 + indra/newview/llbottomtray.h | 1 + indra/newview/llviewerfloaterreg.cpp | 2 + indra/newview/llvoavatar.cpp | 31 +- .../skins/default/xui/en/floater_ao.xml | 39 + .../skins/default/xui/en/menu_bottomtray.xml | 11 + .../skins/default/xui/en/notifications.xml | 47 + .../newview/skins/default/xui/en/panel_ao.xml | 258 +++ .../skins/default/xui/en/panel_bottomtray.xml | 49 +- .../firestorm/xui/en/panel_bottomtray.xml | 49 +- 19 files changed, 3177 insertions(+), 6 deletions(-) create mode 100644 indra/newview/ao.cpp create mode 100644 indra/newview/ao.h create mode 100644 indra/newview/aoengine.cpp create mode 100644 indra/newview/aoengine.h create mode 100644 indra/newview/aoset.cpp create mode 100644 indra/newview/aoset.h create mode 100644 indra/newview/skins/default/xui/en/floater_ao.xml create mode 100644 indra/newview/skins/default/xui/en/panel_ao.xml diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 6581775716..7a8ecbed38 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -70,6 +70,9 @@ include_directories( ) set(viewer_SOURCE_FILES + ao.cpp + aoengine.cpp + aoset.cpp chatbar_as_cmdline.cpp kcwlinterface.cpp floatermedialists.cpp @@ -629,6 +632,9 @@ endif (LINUX) set(viewer_HEADER_FILES CMakeLists.txt ViewerInstall.cmake + ao.h + aoengine.h + aoset.h chatbar_as_cmdline.h kcwlinterface.h floatermedialists.h diff --git a/indra/newview/ao.cpp b/indra/newview/ao.cpp new file mode 100644 index 0000000000..56dea676bc --- /dev/null +++ b/indra/newview/ao.cpp @@ -0,0 +1,487 @@ +/** + * @file ao.cpp + * @brief Anything concerning the Viewer Side Animation Overrider GUI + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2011, Zi Ree @ Second Life + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "ao.h" +#include "aoengine.h" +#include "aoset.h" +#include "llbottomtray.h" +#include "llcheckboxctrl.h" +#include "llcombobox.h" +#include "llnotificationsutil.h" +#include "llspinctrl.h" +#include "llviewerinventory.h" + +FloaterAO::FloaterAO(const LLSD& key) +: LLTransientDockableFloater(NULL,true,key), + mSetList(0), + mSelectedSet(0), + mSelectedState(0), + mCanDragAndDrop(FALSE) +{ +} + +FloaterAO::~FloaterAO() +{ +} + +void FloaterAO::updateSetParameters() +{ + mOverrideSitsCheckBox->setValue(mSelectedSet->getSitOverride()); + mDisableMouselookCheckBox->setValue(mSelectedSet->getMouselookDisable()); + BOOL isDefault=(mSelectedSet==AOEngine::instance().getDefaultSet()) ? TRUE : FALSE; + mDefaultCheckBox->setValue(isDefault); + mDefaultCheckBox->setEnabled(!isDefault); +} + +void FloaterAO::updateAnimationList() +{ + S32 currentStateSelected=mStateSelector->getCurrentIndex(); + + mStateSelector->removeall(); + if(!mSelectedSet) + { + mStateSelector->setEnabled(FALSE); + mStateSelector->add("no_animations_loaded"); // getString("no_animations_loaded")); + return; + } + + for(S32 index=0;indexmStateNames.size();index++) + { + std::string stateName=mSelectedSet->mStateNames[index]; + AOSet::AOState* state=mSelectedSet->getStateByName(stateName); + mStateSelector->add(stateName,state,ADD_BOTTOM,TRUE); + } + + enableStateControls(TRUE); + + if(currentStateSelected==-1) + mStateSelector->selectFirstItem(); + else + mStateSelector->selectNthItem(currentStateSelected); + + onSelectState(); +} + +void FloaterAO::updateList() +{ + std::string currentSetName=mSetSelector->getSelectedItemLabel(); + if(currentSetName.empty()) + currentSetName=AOEngine::instance().getCurrentSetName(); + + mSetList=AOEngine::instance().getSetList(); + mSetSelector->removeall(); + + if(mSetList.empty()) + { + mSetSelector->setEnabled(FALSE); + mSetSelector->add("no_sets_loaded"); // getString("no_sets_loaded")); + return; + } + + for(S32 index=0;indexgetName(); + mSetSelector->add(setName,&mSetList[index],ADD_BOTTOM,TRUE); + if(setName.compare(currentSetName)==0) + { + mSelectedSet=AOEngine::instance().selectSetByName(currentSetName); + mSetSelector->selectNthItem(index); + updateSetParameters(); + updateAnimationList(); + } + } + enableSetControls(TRUE); +} + +BOOL FloaterAO::postBuild() +{ + LLPanel* aoPanel=getChild("animation_overrider_panel"); + mSetSelector=aoPanel->getChild("ao_set_selection_combo"); + mActivateSetButton=aoPanel->getChild("ao_activate"); + mAddButton=aoPanel->getChild("ao_add"); + mRemoveButton=aoPanel->getChild("ao_remove"); + mDefaultCheckBox=aoPanel->getChild("ao_default"); + mOverrideSitsCheckBox=aoPanel->getChild("ao_sit_override"); + mDisableMouselookCheckBox=aoPanel->getChild("ao_disable_stands_in_mouselook"); + + mStateSelector=aoPanel->getChild("ao_state_selection_combo"); + mAnimationList=aoPanel->getChild("ao_state_animation_list"); + mMoveUpButton=aoPanel->getChild("ao_move_up"); + mMoveDownButton=aoPanel->getChild("ao_move_down"); + mTrashButton=aoPanel->getChild("ao_trash"); + mRandomizeCheckBox=aoPanel->getChild("ao_randomize"); + mCycleTimeTextLabel=aoPanel->getChild("ao_cycle_time_seconds_label"); + mCycleTimeSpinner=aoPanel->getChild("ao_cycle_time"); + + mSetSelector->setCommitCallback(boost::bind(&FloaterAO::onSelectSet,this)); + mActivateSetButton->setCommitCallback(boost::bind(&FloaterAO::onClickActivate,this)); + mAddButton->setCommitCallback(boost::bind(&FloaterAO::onClickAdd,this)); + mRemoveButton->setCommitCallback(boost::bind(&FloaterAO::onClickRemove,this)); + mDefaultCheckBox->setCommitCallback(boost::bind(&FloaterAO::onCheckDefault,this)); + mOverrideSitsCheckBox->setCommitCallback(boost::bind(&FloaterAO::onCheckOverrideSits,this)); + mDisableMouselookCheckBox->setCommitCallback(boost::bind(&FloaterAO::onCheckDisableStands,this)); + + mAnimationList->setCommitOnSelectionChange(TRUE); + + mStateSelector->setCommitCallback(boost::bind(&FloaterAO::onSelectState,this)); + mAnimationList->setCommitCallback(boost::bind(&FloaterAO::onChangeAnimationSelection,this)); + mMoveUpButton->setCommitCallback(boost::bind(&FloaterAO::onClickMoveUp,this)); + mMoveDownButton->setCommitCallback(boost::bind(&FloaterAO::onClickMoveDown,this)); + mTrashButton->setCommitCallback(boost::bind(&FloaterAO::onClickTrash,this)); + mRandomizeCheckBox->setCommitCallback(boost::bind(&FloaterAO::onCheckRandomize,this)); + mCycleTimeSpinner->setCommitCallback(boost::bind(&FloaterAO::onChangeCycleTime,this)); + + aoPanel->getChild("ao_reload")->setCommitCallback(boost::bind(&FloaterAO::onClickReload,this)); + aoPanel->getChild("ao_previous")->setCommitCallback(boost::bind(&FloaterAO::onClickPrevious,this)); + aoPanel->getChild("ao_next")->setCommitCallback(boost::bind(&FloaterAO::onClickNext,this)); + + AOEngine::instance().setReloadCallback(boost::bind(&FloaterAO::updateList,this)); + + enableSetControls(FALSE); + + updateList(); + return LLDockableFloater::postBuild(); +} + +void FloaterAO::enableSetControls(BOOL yes) +{ + mSetSelector->setEnabled(yes); + mActivateSetButton->setEnabled(yes); + mDefaultCheckBox->setEnabled(yes); + mOverrideSitsCheckBox->setEnabled(yes); + mDisableMouselookCheckBox->setEnabled(yes); +} + +void FloaterAO::enableStateControls(BOOL yes) +{ + mStateSelector->setEnabled(yes); + mAnimationList->setEnabled(yes); + mRandomizeCheckBox->setEnabled(yes); + mCycleTimeTextLabel->setEnabled(yes); + mCycleTimeSpinner->setEnabled(yes); + mCanDragAndDrop=yes; +} + +void FloaterAO::onOpen(const LLSD& key) +{ + LLButton* anchor_panel=LLBottomTray::instance().getChild("ao_toggle_btn"); + if(anchor_panel) + setDockControl(new LLDockControl(anchor_panel,this,getDockTongue(),LLDockControl::TOP)); +} + +void FloaterAO::onSelectSet() +{ + mSelectedSet=AOEngine::instance().getSetByName(mSetSelector->getSelectedItemLabel()); + if(mSelectedSet) + { + updateSetParameters(); + updateAnimationList(); + } +} + +void FloaterAO::onClickActivate() +{ + llwarns << "Set activated: " << mSetSelector->getSelectedItemLabel() << llendl; + AOEngine::instance().selectSet(mSelectedSet); +} + +LLScrollListItem* FloaterAO::addAnimation(const std::string name) +{ + LLSD row; + row["columns"][0]["type"]="icon"; + row["columns"][0]["value"]="Inv_Animation"; + row["columns"][0]["width"]=20; + + row["columns"][1]["type"]="text"; + row["columns"][1]["value"]=name; + row["columns"][1]["width"]=120; // 170 later + + return mAnimationList->addElement(row); +} + +void FloaterAO::onSelectState() +{ + mAnimationList->deleteAllItems(); + onChangeAnimationSelection(); + + if(!mSelectedSet) + return; + + mSelectedState=mSelectedSet->getStateByName(mStateSelector->getSelectedItemLabel()); + if(!mSelectedState) + { +// mAnimationList->addSimpleElement("no_animations_loaded"); // getString("no_animations_loaded")); + + mAnimationList->setEnabled(FALSE); + LLSD row; +/* row["columns"][0]["type"]="icon"; + row["columns"][0]["value"]=""; + row["columns"][0]["width"]=20; +*/ + row["columns"][0]["type"]="text"; + row["columns"][0]["value"]="no_animations_loaded"; // getString("no_animations_loaded")); + row["columns"][0]["width"]=190; + mAnimationList->addElement(row); + return; + } + + mAnimationList->setEnabled(TRUE); + mSelectedState=(AOSet::AOState*) mStateSelector->getCurrentUserdata(); + for(S32 index=0;indexmAnimations.size();index++) + { + LLScrollListItem* item=addAnimation(mSelectedState->mAnimations[index].mName); + if(item) + item->setUserdata(&mSelectedState->mAnimations[index].mInventoryUUID); + } + + mRandomizeCheckBox->setValue(mSelectedState->mRandom); + mCycleTimeSpinner->setValue(mSelectedState->mCycleTime); +} + +void FloaterAO::onClickReload() +{ + enableSetControls(FALSE); + enableStateControls(FALSE); + + mSelectedSet=0; + mSelectedState=0; + + AOEngine::instance().reload(); + updateList(); +} + +void FloaterAO::onClickAdd() +{ + LLNotificationsUtil::add("NewAOSet",LLSD(),LLSD(),boost::bind(&FloaterAO::newSetCallback,this,_1,_2)); +} + +BOOL FloaterAO::newSetCallback(const LLSD& notification,const LLSD& response) +{ + std::string newSetName=response["message"].asString(); + S32 option=LLNotificationsUtil::getSelectedOption(notification,response); + + if(newSetName=="") + return FALSE; + else if(newSetName.find(":")!=std::string::npos) + { + LLNotificationsUtil::add("NewAOCantContainColon",LLSD()); + return FALSE; + } + + if(option==0) + { + if(AOEngine::instance().addSet(newSetName).notNull()) + { + enableSetControls(FALSE); + enableStateControls(FALSE); + return TRUE; + } + } + return FALSE; +} + +void FloaterAO::onClickRemove() +{ + LLSD args; + args["AO_SET_NAME"]=mSelectedSet->getName(); + LLNotificationsUtil::add("RemoveAOSet",args,LLSD(),boost::bind(&FloaterAO::removeSetCallback,this,_1,_2)); +} + +BOOL FloaterAO::removeSetCallback(const LLSD& notification,const LLSD& response) +{ + std::string newSetName=response["message"].asString(); + S32 option=LLNotificationsUtil::getSelectedOption(notification,response); + + if(option==0) + { + if(AOEngine::instance().removeSet(mSelectedSet)) + { + // to prevent snapping back to deleted set + mSetSelector->removeall(); + enableSetControls(FALSE); + enableStateControls(FALSE); + return TRUE; + } + } + return FALSE; +} + +void FloaterAO::onCheckDefault() +{ + if(mSelectedSet) + AOEngine::instance().setDefaultSet(mSelectedSet); +} + +void FloaterAO::onCheckOverrideSits() +{ + if(mSelectedSet) + AOEngine::instance().setOverrideSits(mSelectedSet,mOverrideSitsCheckBox->getValue().asBoolean()); +} + +void FloaterAO::onCheckDisableStands() +{ + if(mSelectedSet) + AOEngine::instance().setDisableStands(mSelectedSet,mDisableMouselookCheckBox->getValue().asBoolean()); +} + +void FloaterAO::onChangeAnimationSelection() +{ + std::vector list=mAnimationList->getAllSelected(); + llwarns << "Selection count: " << list.size() << llendl; + + BOOL resortEnable=FALSE; + BOOL trashEnable=FALSE; + + if(list.size()>0) + { + if(list.size()==1) + resortEnable=TRUE; + trashEnable=TRUE; + } + + mMoveDownButton->setEnabled(resortEnable); + mMoveUpButton->setEnabled(resortEnable); + mTrashButton->setEnabled(trashEnable); +} + +void FloaterAO::onClickMoveUp() +{ + if(!mSelectedState) + return; + + std::vector list=mAnimationList->getAllSelected(); + if(list.size()!=1) + return; + + S32 currentIndex=mAnimationList->getFirstSelectedIndex(); + if(currentIndex==-1) + return; + + if(AOEngine::instance().swapWithPrevious(mSelectedState,currentIndex)) + mAnimationList->swapWithPrevious(currentIndex); +} + +void FloaterAO::onClickMoveDown() +{ + if(!mSelectedState) + return; + + std::vector list=mAnimationList->getAllSelected(); + if(list.size()!=1) + return; + + S32 currentIndex=mAnimationList->getFirstSelectedIndex(); + if(currentIndex>=(mAnimationList->getItemCount()-1)) + return; + + if(AOEngine::instance().swapWithNext(mSelectedState,currentIndex)) + mAnimationList->swapWithNext(currentIndex); +} + +void FloaterAO::onClickTrash() +{ + if(!mSelectedState) + return; + + std::vector list=mAnimationList->getAllSelected(); + if(list.size()==0) + return; + + for(S32 index=list.size()-1;index!=-1;index--) + AOEngine::instance().removeAnimation(mSelectedSet,mSelectedState,mAnimationList->getItemIndex(list[index])); + + mAnimationList->deleteSelectedItems(); +} + +void FloaterAO::onCheckRandomize() +{ + if(mSelectedState) + AOEngine::instance().setRandomize(mSelectedState,mRandomizeCheckBox->getValue().asBoolean()); +} + +void FloaterAO::onChangeCycleTime() +{ + if(mSelectedState) + AOEngine::instance().setCycleTime(mSelectedState,mCycleTimeSpinner->getValueF32()); +} + +void FloaterAO::onClickPrevious() +{ + AOEngine::instance().cycle(AOEngine::CyclePrevious); +} + +void FloaterAO::onClickNext() +{ + AOEngine::instance().cycle(AOEngine::CycleNext); +} + +// virtual +BOOL FloaterAO::handleDragAndDrop(S32 x,S32 y,MASK mask,BOOL drop,EDragAndDropType type,void* data, + EAcceptance* accept,std::string& tooltipMsg) +{ + if(!mSelectedSet || !mSelectedState || !mCanDragAndDrop) + { + *accept=ACCEPT_NO; + return TRUE; + } + + LLInventoryItem* item=(LLInventoryItem*) data; + + if(type==DAD_ANIMATION) + { + *accept=ACCEPT_YES_MULTI; + if(item && drop) + { + if(AOEngine::instance().addAnimation(mSelectedSet,mSelectedState,item)) + { + addAnimation(item->getName()); + + enableSetControls(FALSE); + enableStateControls(FALSE); + } + } + } + else if(type==DAD_NOTECARD) + { + *accept=ACCEPT_YES_SINGLE; + if(item && drop) + { + llwarns << item->getUUID() << llendl; + if(AOEngine::instance().importNotecard(item)) + { + enableSetControls(FALSE); + enableStateControls(FALSE); + } + } + } + else + *accept=ACCEPT_NO; + + return TRUE; +} diff --git a/indra/newview/ao.h b/indra/newview/ao.h new file mode 100644 index 0000000000..f791a1af96 --- /dev/null +++ b/indra/newview/ao.h @@ -0,0 +1,112 @@ +/** + * @file ao.h + * @brief Anything concerning the Viewer Side Animation Overrider GUI + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2011, Zi Ree @ Second Life + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef AO_H +#define AO_H + +#include "lltransientdockablefloater.h" +#include "aoset.h" + +class LLButton; +class LLComboBox; +class LLCheckBoxCtrl; +class LLScrollListCtrl; +class LLScrollListItem; +class LLSpinCtrl; +class LLTextBox; + +class FloaterAO +: public LLTransientDockableFloater +{ + friend class LLFloaterReg; + + private: + FloaterAO(const LLSD& key); + ~FloaterAO(); + + public: + /*virtual*/ BOOL postBuild(); + virtual void onOpen(const LLSD& key); + void updateList(); + void updateSetParameters(); + void updateAnimationList(); + + BOOL handleDragAndDrop(S32 x,S32 y,MASK mask,BOOL drop,EDragAndDropType cargo_type,void* cargo_data, + EAcceptance* accept,std::string& tooltip_msg); + + protected: + LLScrollListItem* addAnimation(const std::string name); + + void onSelectSet(); + void onSelectState(); + void onChangeAnimationSelection(); + void onClickReload(); + void onClickAdd(); + void onClickRemove(); + void onClickActivate(); + void onCheckDefault(); + void onCheckOverrideSits(); + void onCheckDisableStands(); + void onClickMoveUp(); + void onClickMoveDown(); + void onClickTrash(); + void onCheckRandomize(); + void onChangeCycleTime(); + void onClickPrevious(); + void onClickNext(); + + void enableSetControls(BOOL yes); + void enableStateControls(BOOL yes); + + BOOL newSetCallback(const LLSD& notification,const LLSD& response); + BOOL removeSetCallback(const LLSD& notification,const LLSD& response); + + std::vector mSetList; + AOSet* mSelectedSet; + AOSet::AOState* mSelectedState; + + LLComboBox* mSetSelector; + LLButton* mActivateSetButton; + LLButton* mAddButton; + LLButton* mRemoveButton; + LLCheckBoxCtrl* mDefaultCheckBox; + LLCheckBoxCtrl* mOverrideSitsCheckBox; + LLCheckBoxCtrl* mDisableMouselookCheckBox; + + LLComboBox* mStateSelector; + LLScrollListCtrl* mAnimationList; + LLButton* mMoveUpButton; + LLButton* mMoveDownButton; + LLButton* mTrashButton; + LLCheckBoxCtrl* mRandomizeCheckBox; + LLTextBox* mCycleTimeTextLabel; + LLSpinCtrl* mCycleTimeSpinner; + + BOOL mCanDragAndDrop; +}; + +#endif // AO_H diff --git a/indra/newview/aoengine.cpp b/indra/newview/aoengine.cpp new file mode 100644 index 0000000000..57f24c9831 --- /dev/null +++ b/indra/newview/aoengine.cpp @@ -0,0 +1,1482 @@ +/** + * @file aoengine.cpp + * @brief The core Animation Overrider engine + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Zi Ree @ Second Life + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "llviewerprecompiledheaders.h" + + +#include "roles_constants.h" + +#include "aoengine.h" +#include "aoset.h" + +#include "llagent.h" +#include "llagentcamera.h" +#include "llanimationstates.h" +#include "llassetstorage.h" +#include "llcommon.h" +#include "llinventoryfunctions.h" +#include "llinventorymodel.h" +#include "llinventoryobserver.h" +#include "llstring.h" +#include "llvfs.h" +#include "llviewerinventory.h" + +// is there a global define for this folder somewhere? +#define ROOT_FIRESTORM_FOLDER "#Firestorm" +#define ROOT_AO_FOLDER "#AO" +#include + +const F32 INVENTORY_POLLING_INTERVAL=5.0; + +AOEngine::AOEngine() : + LLSingleton(), + mCurrentSet(0), + mDefaultSet(0), + mEnabled(FALSE), + mInMouselook(FALSE), + mImportSet(0), + mImportCategory(LLUUID::null), + mAOFolder(LLUUID::null), + mLastMotion(ANIM_AGENT_STAND), + mLastOverriddenMotion(ANIM_AGENT_STAND) +{ +} + +AOEngine::~AOEngine() +{ + clear(); +} + +void AOEngine::init() +{ +} + +// static +void AOEngine::onLoginComplete() +{ + AOEngine::instance().init(); +} + +void AOEngine::clear() +{ + mSets.clear(); + mCurrentSet=0; +} + +void AOEngine::stopAllStandVariants() +{ + llwarns << "stopping all STAND variants." << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_STAND_1,ANIM_REQUEST_STOP); + gAgent.sendAnimationRequest(ANIM_AGENT_STAND_2,ANIM_REQUEST_STOP); + gAgent.sendAnimationRequest(ANIM_AGENT_STAND_3,ANIM_REQUEST_STOP); + gAgent.sendAnimationRequest(ANIM_AGENT_STAND_4,ANIM_REQUEST_STOP); + gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_STAND_1); + gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_STAND_2); + gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_STAND_3); + gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_STAND_4); +} + +void AOEngine::stopAllSitVariants() +{ + llwarns << "stopping all SIT variants." << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_SIT_FEMALE,ANIM_REQUEST_STOP); + gAgent.sendAnimationRequest(ANIM_AGENT_SIT_GENERIC,ANIM_REQUEST_STOP); + gAgent.sendAnimationRequest(ANIM_AGENT_SIT_GROUND,ANIM_REQUEST_STOP); + gAgent.sendAnimationRequest(ANIM_AGENT_SIT_GROUND_CONSTRAINED,ANIM_REQUEST_STOP); + gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_SIT_FEMALE); + gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_SIT_GENERIC); + gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_SIT_GROUND); + gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_SIT_GROUND_CONSTRAINED); +} + +void AOEngine::setLastMotion(LLUUID motion) +{ + if(motion!=ANIM_AGENT_TYPE) + mLastMotion=motion; +} + +void AOEngine::setLastOverriddenMotion(LLUUID motion) +{ + if(motion!=ANIM_AGENT_TYPE) + mLastOverriddenMotion=motion; +} + +BOOL AOEngine::foreignAnimations() +{ + for(LLVOAvatar::AnimSourceIterator srcIt=gAgentAvatarp->mAnimationSources.begin(); + srcIt!=gAgentAvatarp->mAnimationSources.end();srcIt++) + { + llwarns << srcIt->first << " - " << srcIt->second << llendl; + if(srcIt->first!=gAgent.getID()) + { + return TRUE; + } + } + return FALSE; +} + +void AOEngine::enable(BOOL yes) +{ + llwarns << "using " << mLastMotion << " enable " << yes << llendl; + mEnabled=yes; + + if(!mCurrentSet) + { + llwarns << "enable(" << yes << ") without animation set loaded." << llendl; + return; + } + + AOSet::AOState* state=mCurrentSet->getStateByRemapID(mLastMotion); + if(mEnabled) + { + if(state && !state->mAnimations.empty()) + { + llwarns << "Enabling animation state " << state->mName << llendl; + + gAgent.sendAnimationRequest(mLastOverriddenMotion,ANIM_REQUEST_STOP); + + LLUUID animation=override(mLastMotion,TRUE); + if(animation.isNull()) + return; + + if(mLastMotion==ANIM_AGENT_STAND) + { + stopAllStandVariants(); + } + else if(mLastMotion==ANIM_AGENT_WALK) + { + llwarns << "Last motion was a WALK, stopping all variants." << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_WALK_NEW,ANIM_REQUEST_STOP); + gAgent.sendAnimationRequest(ANIM_AGENT_FEMALE_WALK,ANIM_REQUEST_STOP); + gAgent.sendAnimationRequest(ANIM_AGENT_FEMALE_WALK_NEW,ANIM_REQUEST_STOP); + gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_WALK_NEW); + gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_FEMALE_WALK); + gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_FEMALE_WALK_NEW); + } + else if(mLastMotion==ANIM_AGENT_RUN) + { + llwarns << "Last motion was a RUN, stopping all variants." << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_RUN_NEW,ANIM_REQUEST_STOP); + gAgent.sendAnimationRequest(ANIM_AGENT_FEMALE_RUN_NEW,ANIM_REQUEST_STOP); + gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_RUN_NEW); + gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_FEMALE_RUN_NEW); + } + else if(mLastMotion==ANIM_AGENT_SIT) + { + stopAllSitVariants(); + } + else + llwarns << "Unhandled last motion id " << mLastMotion << llendl; + + gAgent.sendAnimationRequest(animation,ANIM_REQUEST_START); + } + } + else + { + // stop all overriders, catch leftovers + for(S32 index=0;indexgetState(index); + if(state) + { + LLUUID animation=state->mCurrentAnimationID; + if(animation.notNull()) + { + llwarns << "Stopping leftover animation from state " << index << llendl; + gAgent.sendAnimationRequest(animation,ANIM_REQUEST_STOP); + } + } + else + lldebugs << "state "<< index <<" returned NULL." << llendl; + } + + if(!foreignAnimations()) + gAgent.sendAnimationRequest(mLastMotion,ANIM_REQUEST_START); + llwarns << "stopTimer()" << llendl; + mCurrentSet->stopTimer(); + } +} + +const LLUUID AOEngine::override(const LLUUID motion,BOOL start) +{ + LLUUID animation; + + if(!mEnabled) + { + if(start && mCurrentSet) + { + const AOSet::AOState* state=mCurrentSet->getStateByRemapID(motion); + if(state) + { + setLastMotion(motion); + llwarns << "(disabled AO) setting last motion id to " << gAnimLibrary.animStateToString(mLastMotion) << llendl; + if(!state->mAnimations.empty()) + { + setLastOverriddenMotion(motion); + llwarns << "(disabled AO) setting last overridden motion id to " << gAnimLibrary.animStateToString(mLastOverriddenMotion) << llendl; + } + } + } + return animation; + } + + if(mSets.empty()) + { + lldebugs << "No sets loaded. Skipping overrider." << llendl; + return animation; + } + + if(!mCurrentSet) + { + lldebugs << "No current AO set chosen. Skipping overrider." << llendl; + return animation; + } + + AOSet::AOState* state=mCurrentSet->getStateByRemapID(motion); + if(!state) + { + lldebugs << "No current AO state for motion " << gAnimLibrary.animStateToString(motion) << " - Skipping overrider." << llendl; + return animation; + } + + llwarns << "stopTimer()" << llendl; + mCurrentSet->stopTimer(); + if(start) + { + // Disable start stands in Mouselook + if(mCurrentSet->getMouselookDisable() && + motion==ANIM_AGENT_STAND && + mInMouselook) + { + setLastMotion(motion); + llwarns << "(enabled AO, mouselook stand stopped) setting last motion id to " << gAnimLibrary.animStateToString(mLastMotion) << llendl; + return animation; + } + + // Do not start override sits if not selected + if(!mCurrentSet->getSitOverride() && motion==ANIM_AGENT_SIT) + { + setLastMotion(motion); + llwarns << "(enabled AO, sit override stopped) setting last motion id to " << gAnimLibrary.animStateToString(mLastMotion) << llendl; + return animation; + } + + setLastMotion(motion); + llwarns << "(enabled AO) setting last motion id to " << gAnimLibrary.animStateToString(mLastMotion) << llendl; + + if(!state->mAnimations.empty()) + { + setLastOverriddenMotion(motion); + llwarns << "(enabled AO) setting last overridden motion id to " << gAnimLibrary.animStateToString(mLastOverriddenMotion) << llendl; + } + + // do not remember typing as set-wide motion + if(motion!=ANIM_AGENT_TYPE) + mCurrentSet->setMotion(motion); + + animation=mCurrentSet->getAnimationForState(state); + state->mCurrentAnimationID=animation; + llwarns << "overriding " << gAnimLibrary.animStateToString(motion) + << " with " << animation + << " in state " << state->mName + << " of set " << mCurrentSet->getName() + << " (" << mCurrentSet << ")" << llendl; + + F32 timeout=state->mCycleTime; + llwarns << "Setting cycle timeout for state " << state->mName << " of " << timeout << llendl; + if(timeout>0.0f) + mCurrentSet->startTimer(timeout); + + if(motion==ANIM_AGENT_SIT) + mSitCancelTimer.oneShot(); + } + else + { + animation=state->mCurrentAnimationID; + state->mCurrentAnimationID.setNull(); + + // for typing animaiton, just return the stored animation and don't memorize anything else + if(motion==ANIM_AGENT_TYPE) + return animation; + + if(motion!=mCurrentSet->getMotion()) + { + llwarns << "trying to stop-override motion " << gAnimLibrary.animStateToString(motion) + << " but the current set has motion " << gAnimLibrary.animStateToString(mCurrentSet->getMotion()) << llendl; + return animation; + } + + mCurrentSet->setMotion(LLUUID::null); + + // stop the underlying Linden Lab motion, in case it's still running. + // frequently happens with sits, so we keep it only for those currently. + if(mLastMotion==ANIM_AGENT_SIT) + stopAllSitVariants(); + + llwarns << "stopping cycle timer for motion " << gAnimLibrary.animStateToString(motion) << + " using animation " << animation << + " in state " << state->mName << llendl; + } + + return animation; +} + +void AOEngine::checkSitCancel() +{ + if(foreignAnimations()) + { + LLUUID animation=mCurrentSet->getStateByRemapID(ANIM_AGENT_SIT)->mCurrentAnimationID; + if(animation.notNull()) + { + llwarns << "Stopping sit animation due to foreign animations running" << llendl; + gAgent.sendAnimationRequest(animation,ANIM_REQUEST_STOP); + mSitCancelTimer.stop(); + } + } +} + +void AOEngine::cycleTimeout(const AOSet* set) +{ + if(!mEnabled) + return; + + if(set!=mCurrentSet) + { + llwarns << "cycleTimeout for set " << set->getName() << " but ouot current set is " << mCurrentSet->getName() << llendl; + return; + } + + cycle(CycleAny); +} + +void AOEngine::cycle(eCycleMode cycleMode) +{ + LLUUID motion=mCurrentSet->getMotion(); + + if(motion==ANIM_AGENT_SIT && !mCurrentSet->getSitOverride()) + return; + + if(motion==ANIM_AGENT_STAND && mCurrentSet->getMouselookDisable() && mInMouselook) + return; + + AOSet::AOState* state=mCurrentSet->getStateByRemapID(motion); + if(!state) + { + lldebugs << "cycle without state." << llendl; + return; + } + + LLUUID animation=state->mCurrentAnimationID; + if(!animation.isNull()) + gAgent.sendAnimationRequest(animation,ANIM_REQUEST_STOP); + + if(cycleMode==CycleAny) + { + llwarns << "CycleAny" << llendl; + animation=override(motion,TRUE); + } + else + { + if(cycleMode==CyclePrevious) + { + llwarns << "CyclePrevious" << llendl; + state->mCurrentAnimation--; + if(state->mCurrentAnimation<0) + state->mCurrentAnimation=state->mAnimations.size()-1; + } + else if(cycleMode==CycleNext) + { + llwarns << "CycleNext" << llendl; + state->mCurrentAnimation++; + if(state->mCurrentAnimation==state->mAnimations.size()) + state->mCurrentAnimation=0; + } + animation=state->mAnimations[state->mCurrentAnimation].mAssetUUID; + state->mCurrentAnimationID=animation; + } + + if(!animation.isNull()) + gAgent.sendAnimationRequest(animation,ANIM_REQUEST_START); +} + +void AOEngine::updateSortOrder(AOSet::AOState* state) +{ + for(S32 index=0;indexmAnimations.size();index++) + { + S32 sortOrder=state->mAnimations[index].mSortOrder; + + if(sortOrder!=index) + { + std::ostringstream numStr(""); + numStr << index; + + llwarns << "sort order is " << sortOrder << " but index is " << index + << ", setting sort order description: " << numStr.str() << llendl; + + state->mAnimations[index].mSortOrder=index; + + LLViewerInventoryItem* item=gInventory.getItem(state->mAnimations[index].mInventoryUUID); + if(!item) + { + llwarns << "NULL inventory item found while trying to copy " << state->mAnimations[index].mInventoryUUID << llendl; + continue; + } + LLPointer newItem= + new LLViewerInventoryItem(item); + + llwarns << newItem->getUUID() << " " << newItem->getName() << llendl; + + newItem->setDescription(numStr.str()); + newItem->setComplete(TRUE); + newItem->updateServer(FALSE); + + gInventory.updateItem(newItem); + } + } +} + +LLUUID AOEngine::addSet(const std::string name,BOOL reload) +{ + llwarns << "adding set folder " << name << llendl; + LLUUID newUUID=gInventory.createNewCategory(mAOFolder,LLFolderType::FT_NONE,name); + + if(reload) + mTimerCollection.enableReloadTimer(TRUE); + return newUUID; +} + +BOOL AOEngine::createAnimationLink(const AOSet* set,AOSet::AOState* state,const LLInventoryItem* item) +{ + llwarns << "Asset ID " << item->getAssetUUID() << " inventory id " << item->getUUID() << " category id " << state->mInventoryUUID << llendl; + if(state->mInventoryUUID.isNull()) + { + llwarns << "no " << state->mName << " folder yet. Creating ..." << llendl; + gInventory.createNewCategory(set->getInventoryUUID(),LLFolderType::FT_NONE,state->mName); + + llwarns << "looking for folder to get UUID ..." << llendl; + + LLUUID newStateFolderUUID; + + LLInventoryModel::item_array_t* items; + LLInventoryModel::cat_array_t* cats; + gInventory.getDirectDescendentsOf(set->getInventoryUUID(),cats,items); + + for(S32 index=0;indexcount();index++) + { + if(cats->get(index)->getName().compare(state->mName)==0) + { + llwarns << "UUID found!" << llendl; + newStateFolderUUID=cats->get(index)->getUUID(); + state->mInventoryUUID=newStateFolderUUID; + break; + } + } + } + + if(state->mInventoryUUID.isNull()) + { + llwarns << "state inventory UUID not found, failing." << llendl; + return FALSE; + } + + link_inventory_item( + gAgent.getID(), + item->getUUID(), + state->mInventoryUUID, + item->getName(), + item->getDescription(), + LLAssetType::AT_LINK, + NULL); + + return TRUE; +} + +BOOL AOEngine::addAnimation(const AOSet* set,AOSet::AOState* state,const LLInventoryItem* item,BOOL reload) +{ + AOSet::AOAnimation anim; + anim.mAssetUUID=item->getAssetUUID(); + anim.mInventoryUUID=item->getUUID(); + anim.mName=item->getName(); + anim.mSortOrder=state->mAnimations.size()+1; + state->mAnimations.push_back(anim); + + createAnimationLink(set,state,item); + + if(reload) + mTimerCollection.enableReloadTimer(TRUE); + return TRUE; +} + +void AOEngine::purgeFolder(LLUUID uuid) +{ + // trash it + remove_category(&gInventory,uuid); + + // clean it + gInventory.purgeDescendentsOf(uuid); + gInventory.notifyObservers(); + + // purge it + gInventory.purgeObject(uuid); + gInventory.notifyObservers(); +} + +BOOL AOEngine::removeSet(AOSet* set) +{ + purgeFolder(set->getInventoryUUID()); + mTimerCollection.enableReloadTimer(TRUE); + return TRUE; +} + +BOOL AOEngine::removeAnimation(const AOSet* set,AOSet::AOState* state,S32 index) +{ + S32 numOfAnimations=state->mAnimations.size(); + if(numOfAnimations==0) + return FALSE; + + llwarns << __LINE__ << " purging: " << state->mAnimations[index].mInventoryUUID << llendl; + gInventory.purgeObject(state->mAnimations[index].mInventoryUUID); // item->getUUID()); + gInventory.notifyObservers(); + + state->mAnimations.erase(state->mAnimations.begin()+index); + + if(state->mAnimations.size()==0) + { + llwarns << "purging folder " << state->mName << " from inventory because it's empty." << llendl; + + LLInventoryModel::item_array_t* items; + LLInventoryModel::cat_array_t* cats; + gInventory.getDirectDescendentsOf(set->getInventoryUUID(),cats,items); + + for(S32 index=0;indexcount();index++) + { + std::vector params; + LLStringUtil::getTokens(cats->get(index)->getName(),params,":"); + std::string stateName=params[0]; + + if(state->mName.compare(stateName)==0) + { + llwarns << "folder found: " << cats->get(index)->getName() << " purging uuid " << cats->get(index)->getUUID() << llendl; + + purgeFolder(cats->get(index)->getUUID()); + state->mInventoryUUID.setNull(); + break; + } + } + } + else + updateSortOrder(state); + + return TRUE; +} + +BOOL AOEngine::swapWithPrevious(AOSet::AOState* state,S32 index) +{ + S32 numOfAnimations=state->mAnimations.size(); + if(numOfAnimations<2 || index==0) + return FALSE; + + AOSet::AOAnimation tmpAnim=state->mAnimations[index]; + state->mAnimations.erase(state->mAnimations.begin()+index); + state->mAnimations.insert(state->mAnimations.begin()+index-1,tmpAnim); + + updateSortOrder(state); + + return TRUE; +} + +BOOL AOEngine::swapWithNext(AOSet::AOState* state,S32 index) +{ + S32 numOfAnimations=state->mAnimations.size(); + if(numOfAnimations<2 || index==(numOfAnimations-1)) + return FALSE; + + AOSet::AOAnimation tmpAnim=state->mAnimations[index]; + state->mAnimations.erase(state->mAnimations.begin()+index); + state->mAnimations.insert(state->mAnimations.begin()+index+1,tmpAnim); + + updateSortOrder(state); + + return TRUE; +} + +void AOEngine::reloadStateAnimations(AOSet::AOState* state) +{ + LLInventoryModel::item_array_t* items; + LLInventoryModel::cat_array_t* dummy; + + state->mAnimations.clear(); + + gInventory.getDirectDescendentsOf(state->mInventoryUUID,dummy,items); + for(S32 num=0;numcount();num++) + { + llwarns << "Found animation link " << items->get(num)->LLInventoryItem::getName() + << " desc " << items->get(num)->LLInventoryItem::getDescription() + << " asset " << items->get(num)->getAssetUUID() << llendl; + + AOSet::AOAnimation anim; + anim.mAssetUUID=items->get(num)->getAssetUUID(); + LLViewerInventoryItem* linkedItem=items->get(num)->getLinkedItem(); + if(linkedItem==0) + { + llwarns << "linked item for link " << items->get(num)->LLInventoryItem::getName() << " not found. Skipping." << llendl; + continue; + } + anim.mName=linkedItem->LLInventoryItem::getName(); + anim.mInventoryUUID=items->get(num)->getUUID(); + + S32 sortOrder; + if(!LLStringUtil::convertToS32(items->get(num)->LLInventoryItem::getDescription(),sortOrder)) + sortOrder=-1; + anim.mSortOrder=sortOrder; + + llwarns << "current sort order is " << sortOrder << llendl; + + if(sortOrder==-1) + { + llwarns << "sort order was unknown so append to the end of the list" << llendl; + state->mAnimations.push_back(anim); + } + else + { + BOOL inserted=FALSE; + for(S32 index=0;indexmAnimations.size();index++) + { + if(state->mAnimations[index].mSortOrder>sortOrder) + { + llwarns << "inserting at index " << index << llendl; + state->mAnimations.insert(state->mAnimations.begin()+index,anim); + inserted=TRUE; + break; + } + } + if(!inserted) + { + llwarns << "not inserted yet, appending to the list instead" << llendl; + state->mAnimations.push_back(anim); + } + } + llwarns << "Animation count now: " << state->mAnimations.size() << llendl; + } + + updateSortOrder(state); +} + +void AOEngine::update() +{ + if(mAOFolder.isNull()) + return; + + LLInventoryModel::cat_array_t* categories; + LLInventoryModel::item_array_t* items; + + BOOL allComplete=TRUE; + mTimerCollection.enableSettingsTimer(FALSE); + + gInventory.getDirectDescendentsOf(mAOFolder,categories,items); + for(S32 index=0;indexcount();index++) + { + LLViewerInventoryCategory* currentCategory=categories->get(index); + const std::string& setFolderName=currentCategory->getName(); + std::vector params; + LLStringUtil::getTokens(setFolderName,params,":"); + + AOSet* newSet=getSetByName(params[0]); + if(newSet==0) + { + lldebugs << "Adding set " << setFolderName << " to AO." << llendl; + newSet=new AOSet(currentCategory->getUUID()); + newSet->setName(params[0]); + mSets.push_back(newSet); + } + else + { + if(newSet->getComplete()) + { + lldebugs << "Set " << params[0] << " already complete. Skipping." << llendl; + continue; + } + lldebugs << "Updating set " << setFolderName << " in AO." << llendl; + } + allComplete=FALSE; + + for(S32 num=1;numsetSitOverride(TRUE); + else if(params[num]=="DM") + newSet->setMouselookDisable(TRUE); + else if(params[num]=="**") + { + mDefaultSet=newSet; + mCurrentSet=newSet; + } + else + llwarns << "Unknown AO set option " << params[num] << llendl; + } + + if(gInventory.isCategoryComplete(currentCategory->getUUID())) + { + lldebugs << "Set " << params[0] << " is complete, reading states ..." << llendl; + + LLInventoryModel::cat_array_t* stateCategories; + gInventory.getDirectDescendentsOf(currentCategory->getUUID(),stateCategories,items); + newSet->setComplete(TRUE); + + for(S32 index=0;indexcount();index++) + { + std::vector params; + LLStringUtil::getTokens(stateCategories->get(index)->getName(),params,":"); + std::string stateName=params[0]; + + AOSet::AOState* state=newSet->getStateByName(stateName); + if(state==NULL) + { + llwarns << "Unknown state " << stateName << ". Skipping." << llendl; + continue; + } + lldebugs << "Reading state " << stateName << llendl; + + state->mInventoryUUID=stateCategories->get(index)->getUUID(); + for(S32 num=1;nummRandom=TRUE; + lldebugs << "Random on" << llendl; + } + else if(params[num].substr(0,2)=="CT") + { + LLStringUtil::convertToS32(params[num].substr(2,params[num].size()-2),state->mCycleTime); + lldebugs << "Cycle Time specified:" << state->mCycleTime << llendl; + } + else + llwarns << "Unknown AO set option " << params[num] << llendl; + } + + if(!gInventory.isCategoryComplete(state->mInventoryUUID)) + { + llwarns << "State category " << stateName << " is incomplete, fetching descendents" << llendl; + gInventory.fetchDescendentsOf(state->mInventoryUUID); + allComplete=FALSE; + newSet->setComplete(FALSE); + continue; + } + reloadStateAnimations(state); + } + } + else + { + llwarns << "Set " << params[0] << " is incomplete, fetching descendents" << llendl; + gInventory.fetchDescendentsOf(currentCategory->getUUID()); + } + } + + if(allComplete) + { + if(!mCurrentSet && !mSets.empty()) + { + llwarns << "No default set defined, choosing the first in the list." << llendl; + mCurrentSet=mSets[0]; + } + mTimerCollection.enableInventoryTimer(FALSE); + mTimerCollection.enableSettingsTimer(TRUE); + + // this should be a preferences option + mEnabled=TRUE; + selectSet(mCurrentSet); + + llwarns << "sending update signal" << llendl; + mUpdatedSignal(); + } +} + +void AOEngine::reload() +{ + BOOL wasEnabled=mEnabled; + + mTimerCollection.enableReloadTimer(FALSE); + + if(wasEnabled) + enable(FALSE); + + gAgent.stopCurrentAnimations(); + mLastOverriddenMotion=ANIM_AGENT_STAND; + + clear(); + mTimerCollection.enableInventoryTimer(TRUE); + update(); + + if(wasEnabled) + enable(TRUE); +} + +AOSet* AOEngine::getSetByName(const std::string name) +{ + AOSet* found=0; + for(S32 index=0;indexgetName().compare(name)==0) + { + found=mSets[index]; + break; + } + } + return found; +} + +const std::string AOEngine::getCurrentSetName() const +{ + if(mCurrentSet) + return mCurrentSet->getName(); + return std::string(); +} + +const AOSet* AOEngine::getDefaultSet() const +{ + return mDefaultSet; +} + +void AOEngine::selectSet(AOSet* set) +{ + BOOL wasEnabled=mEnabled; + if(wasEnabled) + enable(FALSE); + + gAgent.stopCurrentAnimations(); + mLastOverriddenMotion=ANIM_AGENT_STAND; + + mCurrentSet=set; + llwarns << "Selected AO set " << set->getName() << llendl; + + if(wasEnabled) + enable(TRUE); +} + +AOSet* AOEngine::selectSetByName(const std::string name) +{ + AOSet* set=getSetByName(name); + if(set) + { + selectSet(set); + return set; + } + llwarns << "Could not find AO set " << name << llendl; + return NULL; +} + +const std::vector AOEngine::getSetList() const +{ + return mSets; +} + +void AOEngine::saveSet(const AOSet* set) +{ + if(!set) + return; + + std::string setParams=set->getName(); + if(set->getSitOverride()) + setParams+=":SO"; + if(set->getMouselookDisable()) + setParams+=":DM"; + if(set==mDefaultSet) + setParams+=":**"; + +/* + // This works fine, but LL seems to have added a few helper functions in llinventoryfunctions.h + // so let's make use of them. This code is just for reference + + LLViewerInventoryCategory* cat=gInventory.getCategory(set->getInventoryUUID()); + llwarns << cat << llendl; + cat->rename(setParams); + cat->updateServer(FALSE); + gInventory.addChangedMask(LLInventoryObserver::LABEL, cat->getUUID()); + gInventory.notifyObservers(); +*/ + rename_category(&gInventory,set->getInventoryUUID(),setParams); +} + +void AOEngine::saveState(const AOSet::AOState* state) +{ + std::string stateParams=state->mName; + F32 time=state->mCycleTime; + if(time>0.0) + { + std::ostringstream timeStr; + timeStr << ":CT" << state->mCycleTime; + stateParams+=timeStr.str(); + } + if(state->mRandom) + stateParams+=":RN"; + + rename_category(&gInventory,state->mInventoryUUID,stateParams); +} + +void AOEngine::saveSettings() +{ + for(S32 index=0;indexgetDirty()) + { + saveSet(set); + llwarns << "dirty set saved " << set->getName() << llendl; + set->setDirty(FALSE); + } + + for(S32 stateIndex=0;stateIndexgetState(stateIndex); + if(state->mDirty) + { + saveState(state); + llwarns << "dirty state saved " << state->mName << llendl; + state->mDirty=FALSE; + } + } + } +} + +void AOEngine::inMouselook(BOOL yes) +{ + if(mInMouselook==yes) + return; + + mInMouselook=yes; + + llwarns << "mouselook mode " << yes << llendl; + if(!mCurrentSet) + return; + + if(!mCurrentSet->getMouselookDisable()) + return; + + if(!mEnabled) + return; + + if(mLastMotion!=ANIM_AGENT_STAND) + return; + + if(yes) + { + AOSet::AOState* state=mCurrentSet->getState(AOSet::Standing); + if(!state) + return; + + LLUUID animation=state->mCurrentAnimationID; + if(!animation.isNull()) + { + gAgent.sendAnimationRequest(animation,ANIM_REQUEST_STOP); + llwarns << " stopped animation " << animation << " in state " << state->mName << llendl; + } + gAgent.sendAnimationRequest(ANIM_AGENT_STAND,ANIM_REQUEST_START); + } + else + { + stopAllStandVariants(); + gAgent.sendAnimationRequest(override(ANIM_AGENT_STAND,TRUE),ANIM_REQUEST_START); + } +} + +void AOEngine::setDefaultSet(AOSet* set) +{ + mDefaultSet=set; + for(S32 index=0;indexsetDirty(TRUE); + + llwarns << "default set now " << set << llendl; +} + +void AOEngine::setOverrideSits(AOSet* set,BOOL yes) +{ + set->setSitOverride(yes); + set->setDirty(TRUE); + + if(mLastMotion!=ANIM_AGENT_SIT) + return; + + if(yes) + { + stopAllSitVariants(); + gAgent.sendAnimationRequest(override(ANIM_AGENT_SIT,TRUE),ANIM_REQUEST_START); + } + else + { + AOSet::AOState* state=mCurrentSet->getState(AOSet::Sitting); + if(!state) + return; + + LLUUID animation=state->mCurrentAnimationID; + if(!animation.isNull()) + gAgent.sendAnimationRequest(animation,ANIM_REQUEST_STOP); + + gAgent.sendAnimationRequest(ANIM_AGENT_SIT,ANIM_REQUEST_START); + } +} + +void AOEngine::setDisableStands(AOSet* set,BOOL yes) +{ + set->setMouselookDisable(yes); + set->setDirty(TRUE); + llwarns << "disable stands in mouselook now " << yes << llendl; + + // make sure an update happens if needed + mInMouselook=!gAgentCamera.cameraMouselook(); + inMouselook(!mInMouselook); +} + +void AOEngine::setRandomize(AOSet::AOState* state,BOOL yes) +{ + state->mRandom=yes; + state->mDirty=TRUE; + llwarns << "randomize now " << yes << llendl; +} + +void AOEngine::setCycleTime(AOSet::AOState* state,F32 time) +{ + state->mCycleTime=time; + state->mDirty=TRUE; + llwarns << "cycle time now " << time << llendl; +} + +void AOEngine::tick() +{ + const LLUUID categoryID=gInventory.findCategoryByName(ROOT_FIRESTORM_FOLDER); + llwarns << "tick()" << categoryID << llendl; + if(categoryID.isNull()) + { + llwarns << "no " << ROOT_FIRESTORM_FOLDER << " folder yet. Creating ..." << llendl; + gInventory.createNewCategory(gInventory.getRootFolderID(),LLFolderType::FT_NONE,ROOT_FIRESTORM_FOLDER); + } + else + { + LLInventoryModel::cat_array_t* categories; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(categoryID,categories,items); + llwarns << "cat " << categories->count() << " items " << items->count() << llendl; + + for(S32 index=0;indexcount();index++) + { + const std::string& catName=categories->get(index)->getName(); + if(catName.compare(ROOT_AO_FOLDER)==0) + { + mAOFolder=categories->get(index)->getUUID(); + break; + } + } + + if(mAOFolder.isNull()) + { + llwarns << "no " << ROOT_AO_FOLDER << " folder yet. Creating ..." << llendl; + gInventory.createNewCategory(categoryID,LLFolderType::FT_NONE,ROOT_AO_FOLDER); + } + else + { + llwarns << "AO basic folder structure intact." << llendl; +// LLInventoryModel::cat_array_t* sets; +// gInventory.getDirectDescendentsOf(categoryID,sets,items); + update(); + } + } +} + +BOOL AOEngine::importNotecard(const LLInventoryItem* item) +{ + if(item) + { + llwarns << "importing notecard: " << item->getName() << llendl; + if(getSetByName(item->getName())) + { + llwarns << "set with this name already exists" << llendl; + return FALSE; + } + + if(!gAgent.allowOperation(PERM_COPY,item->getPermissions(),GP_OBJECT_MANIPULATE) && !gAgent.isGodlike()) + { + llwarns << "Insufficient permissions to read notecard." << llendl; + return FALSE; + } + + if(item->getAssetUUID().notNull()) + { + mImportSet=new AOSet(item->getParentUUID()); + if(!mImportSet) + { + llwarns << "could not create import set." << llendl; + return FALSE; + } + mImportSet->setName(item->getName()); + + LLUUID* newUUID=new LLUUID(item->getAssetUUID()); + const LLHost sourceSim=LLHost::invalid; + + gAssetStorage->getInvItemAsset + ( + sourceSim, + gAgent.getID(), + gAgent.getSessionID(), + item->getPermissions().getOwner(), + LLUUID::null, + item->getUUID(), + item->getAssetUUID(), + item->getType(), + &onNotecardLoadComplete, + (void*) newUUID, + TRUE + ); + + return TRUE; + } + } + return FALSE; +} + +// static +void AOEngine::onNotecardLoadComplete( LLVFS* vfs,const LLUUID& assetUUID,LLAssetType::EType type, + void* userdata,S32 status,LLExtStat extStatus) +{ + if(status!=LL_ERR_NOERR) + { + llwarns << "Error downloading import notecard." << llendl; + // NULL tells the importer to cancel all operations and free the import set memory + AOEngine::instance().parseNotecard(NULL); + return; + } + llwarns << "Downloading import notecard complete." << llendl; + + S32 notecardSize=vfs->getSize(assetUUID,type); + char* buffer=new char[notecardSize]; + vfs->getData(assetUUID,type,(U8*) buffer,0,notecardSize); + + AOEngine::instance().parseNotecard(buffer); +} + +void AOEngine::parseNotecard(const char* buffer) +{ + llwarns << "parsing import notecard" << llendl; + + BOOL isValid=FALSE; + + if(!buffer) + { + llwarns << "buffer==NULL - aborting import" << llendl; + // NOTE: cleanup is always the same, needs streamlining + delete mImportSet; + mImportSet=0; + mUpdatedSignal(); + return; + } + + std::string text(buffer); + delete buffer; + + std::vector lines; + LLStringUtil::getTokens(text,lines,"\n"); + + S32 found=-1; + for(S32 index=0;indexgetInventoryUUID()); + if(!importCategory) + { + llwarns << "couldn't find folder to read the animations" << llendl; + delete mImportSet; + mImportSet=0; + mUpdatedSignal(); + return; + } + + std::map animationMap; + LLInventoryModel::cat_array_t* dummy; + LLInventoryModel::item_array_t* items; + + gInventory.getDirectDescendentsOf(mImportSet->getInventoryUUID(),dummy,items); + for(S32 index=0;indexsize();index++) + { + animationMap[items->get(index)->getName()]=items->get(index)->getUUID(); + llwarns << "animation " << items->get(index)->getName() << + " has inventory UUID " << animationMap[items->get(index)->getName()] << llendl; + } + + // [ State ]Anim1|Anim2|Anim3 + for(S32 index=found+1;indexgetStateByName(stateName); + if(!newState) + { + llwarns << "state name " << stateName << " not found." << llendl; + continue; + } + + std::string animationLine=line.substr(endTag+1); + std::vector animationList; + LLStringUtil::getTokens(animationLine,animationList,"|"); + + for(S32 animIndex=0;animIndexmAnimations.push_back(animation); + isValid=TRUE; + } + } + + if(!isValid) + { + llwarns << "Notecard didn't contain any usable data. Aborting import." << llendl; + // NOTE: cleanup is always the same, needs streamlining + delete mImportSet; + mImportSet=0; + mUpdatedSignal(); + return; + } + + mTimerCollection.enableImportTimer(TRUE); + mImportRetryCount=0; + processImport(); +} + +void AOEngine::processImport() +{ + if(mImportCategory.isNull()) + { + mImportCategory=addSet(mImportSet->getName(),FALSE); + if(mImportCategory.isNull()) + { + mImportRetryCount++; + if(mImportRetryCount==5) + { + // NOTE: cleanup is the same as at the end of this function. Needs streamlining. + mTimerCollection.enableImportTimer(FALSE); + delete mImportSet; + mImportSet=0; + mImportCategory.setNull(); + mUpdatedSignal(); + llwarns << "could not create import category for set " << mImportSet->getName() << " ... giving up" << llendl; + } + else + llwarns << "could not create import category for set " << mImportSet->getName() << " ... retrying ..." << llendl; + return; + } + mImportSet->setInventoryUUID(mImportCategory); + } + + BOOL allComplete=TRUE; + for(S32 index=0;indexgetState(index); + if(state->mAnimations.size()) + { + allComplete=FALSE; + llwarns << "state " << state->mName << " still has animations to link." << llendl; + + for(S32 animationIndex=state->mAnimations.size()-1;animationIndex>=0;animationIndex--) + { + llwarns << "linking animation " << state->mAnimations[animationIndex].mName << llendl; + if(createAnimationLink(mImportSet,state,gInventory.getItem(state->mAnimations[animationIndex].mInventoryUUID))) + { + llwarns << "link success, size "<< state->mAnimations.size() << ", removing animation " << + (*(state->mAnimations.begin()+animationIndex)).mName << " from import state" << llendl; + state->mAnimations.erase(state->mAnimations.begin()+animationIndex); + llwarns << "deleted, size now: " << state->mAnimations.size() << llendl; + } + else + llwarns << "link failed!" << llendl; + } + } + } + + if(allComplete) + { + mTimerCollection.enableImportTimer(FALSE); + delete mImportSet; + mImportSet=0; + mImportCategory.setNull(); + reload(); + } +} + +// ---------------------------------------------------- + +AOSitCancelTimer::AOSitCancelTimer() +: LLEventTimer(0.1), + mTickCount(0) +{ + mEventTimer.stop(); +} + +AOSitCancelTimer::~AOSitCancelTimer() +{ +} + +void AOSitCancelTimer::oneShot() +{ + mTickCount=0; + mEventTimer.start(); +} + +void AOSitCancelTimer::stop() +{ + mEventTimer.stop(); +} + +BOOL AOSitCancelTimer::tick() +{ + mTickCount++; + AOEngine::instance().checkSitCancel(); + if(mTickCount==10) + mEventTimer.stop(); + return FALSE; +} + +// ---------------------------------------------------- + +AOTimerCollection::AOTimerCollection() +: LLEventTimer(INVENTORY_POLLING_INTERVAL), + mInventoryTimer(TRUE), + mSettingsTimer(FALSE), + mReloadTimer(FALSE), + mImportTimer(FALSE) +{ + updateTimers(); +} + +AOTimerCollection::~AOTimerCollection() +{ +} + +BOOL AOTimerCollection::tick() +{ + if(mInventoryTimer) + { + llwarns << "Inventory timer tick()" << llendl; + AOEngine::instance().tick(); + } + if(mSettingsTimer) + { + llwarns << "Settings timer tick()" << llendl; + AOEngine::instance().saveSettings(); + } + if(mReloadTimer) + { + llwarns << "Reload timer tick()" << llendl; + AOEngine::instance().reload(); + } + if(mImportTimer) + { + llwarns << "Import timer tick()" << llendl; + AOEngine::instance().processImport(); + } + +// always return FALSE or the LLEventTimer will be deleted -> crash + return FALSE; +} + +void AOTimerCollection::enableInventoryTimer(BOOL yes) +{ + mInventoryTimer=yes; + updateTimers(); +} + +void AOTimerCollection::enableSettingsTimer(BOOL yes) +{ + mSettingsTimer=yes; + updateTimers(); +} + +void AOTimerCollection::enableReloadTimer(BOOL yes) +{ + mReloadTimer=yes; + updateTimers(); +} + +void AOTimerCollection::enableImportTimer(BOOL yes) +{ + mImportTimer=yes; + updateTimers(); +} + +void AOTimerCollection::updateTimers() +{ + if(!mInventoryTimer && !mSettingsTimer && !mReloadTimer && !mImportTimer) + { + llwarns << "no timer needed, stopping internal timer." << llendl; + mEventTimer.stop(); + } + else + { + llwarns << "timer needed, starting internal timer." << llendl; + mEventTimer.start(); + } +} diff --git a/indra/newview/aoengine.h b/indra/newview/aoengine.h new file mode 100644 index 0000000000..d34feac5e2 --- /dev/null +++ b/indra/newview/aoengine.h @@ -0,0 +1,188 @@ +/** + * @file aoengine.h + * @brief The core Animation Overrider engine + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Zi Ree @ Second Life + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AOENGINE_H +#define AOENGINE_H + +#include + +#include "aoset.h" + +#include "llassettype.h" +#include "lleventtimer.h" +#include "llsingleton.h" +#include "lluuid.h" + +class AOTimerCollection +: public LLEventTimer +{ + public: + AOTimerCollection(); + ~AOTimerCollection(); + + virtual BOOL tick(); + + void enableInventoryTimer(BOOL yes); + void enableSettingsTimer(BOOL yes); + void enableReloadTimer(BOOL yes); + void enableImportTimer(BOOL yes); + + protected: + void updateTimers(); + + BOOL mInventoryTimer; + BOOL mSettingsTimer; + BOOL mReloadTimer; + BOOL mImportTimer; +}; + +// ---------------------------------------------------- + +class AOSitCancelTimer +: public LLEventTimer +{ + public: + AOSitCancelTimer(); + ~AOSitCancelTimer(); + + void oneShot(); + void stop(); + + virtual BOOL tick(); + + protected: + S32 mTickCount; +}; + +// ---------------------------------------------------- + +class AOState; +class LLInventoryItem; +class LLVFS; + +class AOEngine +: public LLSingleton +{ + friend class LLSingleton; + + private: + AOEngine(); + ~AOEngine(); + + public: + enum eCycleMode + { + CycleAny, + CycleNext, + CyclePrevious + }; + + void enable(BOOL yes); + const LLUUID override(const LLUUID motion,BOOL start); + void tick(); + void update(); + void reload(); + void reloadStateAnimations(AOSet::AOState* state); + void clear(); + + LLUUID addSet(const std::string name,BOOL reload=TRUE); + BOOL removeSet(AOSet* set); + + BOOL addAnimation(const AOSet* set,AOSet::AOState* state,const LLInventoryItem* item,BOOL reload=TRUE); + BOOL removeAnimation(const AOSet* set,AOSet::AOState* state,S32 index); + void checkSitCancel(); + + BOOL importNotecard(const LLInventoryItem* item); + void processImport(); + + BOOL swapWithPrevious(AOSet::AOState* state,S32 index); + BOOL swapWithNext(AOSet::AOState* state,S32 index); + + void cycleTimeout(const AOSet* set); + void cycle(eCycleMode cycleMode); + + void inMouselook(BOOL yes); + void selectSet(AOSet* set); + AOSet* selectSetByName(const std::string name); + AOSet* getSetByName(const std::string name); + + // callback from LLAppViewer + static void onLoginComplete(); + + const std::vector getSetList() const; + const std::string getCurrentSetName() const; + const AOSet* getDefaultSet() const; + + void setDefaultSet(AOSet* set); + void setOverrideSits(AOSet* set,BOOL yes); + void setDisableStands(AOSet* set,BOOL yes); + void setRandomize(AOSet::AOState* state,BOOL yes); + void setCycleTime(AOSet::AOState* state,F32 time); + + void saveSettings(); + + typedef boost::signals2::signal updated_signal_t; + boost::signals2::connection setReloadCallback(const updated_signal_t::slot_type& cb) { return mUpdatedSignal.connect(cb); }; + + protected: + void init(); + + void setLastMotion(LLUUID motion); + void setLastOverriddenMotion(LLUUID motion); + + void stopAllStandVariants(); + void stopAllSitVariants(); + + BOOL foreignAnimations(); + + void updateSortOrder(AOSet::AOState* state); + void saveSet(const AOSet* set); + void saveState(const AOSet::AOState* state); + + BOOL createAnimationLink(const AOSet* set,AOSet::AOState* state,const LLInventoryItem* item); + void purgeFolder(LLUUID uuid); + + static void onNotecardLoadComplete( LLVFS* vfs,const LLUUID& assetUUID,LLAssetType::EType type, + void* userdata,S32 status,LLExtStat extStatus); + void parseNotecard(const char* buffer); + + updated_signal_t mUpdatedSignal; + + AOTimerCollection mTimerCollection; + AOSitCancelTimer mSitCancelTimer; + + BOOL mEnabled; + BOOL mInMouselook; + LLUUID mAOFolder; + LLUUID mLastMotion; + LLUUID mLastOverriddenMotion; + std::vector mSets; + AOSet* mCurrentSet; + AOSet* mDefaultSet; + + AOSet* mImportSet; + LLUUID mImportCategory; + S32 mImportRetryCount; +}; + +#endif // AOENGINE_H diff --git a/indra/newview/aoset.cpp b/indra/newview/aoset.cpp new file mode 100644 index 0000000000..55f7baef5b --- /dev/null +++ b/indra/newview/aoset.cpp @@ -0,0 +1,263 @@ +/** + * @file aoset.cpp + * @brief Implementation of an Animation Overrider set of animations + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Zi Ree @ Second Life + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "llviewerprecompiledheaders.h" + +#include "aoengine.h" +#include "aoset.h" +#include "llanimationstates.h" + +AOSet::AOSet(const LLUUID inventoryID) +: LLEventTimer(10000.0f), + mInventoryID(inventoryID), + mName("** New AO Set **"), + mSitOverride(FALSE), + mMouselookDisable(FALSE), + mComplete(FALSE), + mDirty(FALSE), + mCurrentMotion(LLUUID()) +{ + llwarns << "Creating new AO set: " << this << llendl; + + // keep number and order in sync with the enum in the declaration + const std::string stateNames[AOSTATES_MAX]= + { + "Standing", + "Walking", + "Running", + "Sitting", + "Sitting On Ground", + "Crouching", + "Crouch Walking", + "Landing", + "Standing Up", + "Falling", + "Flying Down", + "Flying Up", + "Flying", + "Flying Slow", + "Hovering", + "Jumping", + "Pre Jumping", + "Turning Right", + "Turning Left", + "Typing", + "Floating", + "Swimming Forward", + "Swimming Up", + "Swimming Down" + }; + + // keep number and order in sync with the enum in the declaration + const LLUUID stateUUIDs[AOSTATES_MAX]= + { + ANIM_AGENT_STAND, + ANIM_AGENT_WALK, + ANIM_AGENT_RUN, + ANIM_AGENT_SIT, + ANIM_AGENT_SIT_GROUND, + ANIM_AGENT_CROUCH, + ANIM_AGENT_CROUCHWALK, + ANIM_AGENT_LAND, + ANIM_AGENT_STANDUP, + ANIM_AGENT_FALLDOWN, + ANIM_AGENT_HOVER_DOWN, + ANIM_AGENT_HOVER_UP, + ANIM_AGENT_FLY, + ANIM_AGENT_FLYSLOW, + ANIM_AGENT_HOVER, + ANIM_AGENT_JUMP, + ANIM_AGENT_PRE_JUMP, + ANIM_AGENT_TURNRIGHT, + ANIM_AGENT_TURNLEFT, + ANIM_AGENT_TYPE, + ANIM_AGENT_HOVER, // needs special treatment + ANIM_AGENT_FLY, // needs special treatment + ANIM_AGENT_HOVER_UP, // needs special treatment + ANIM_AGENT_HOVER_DOWN // needs special treatment + }; + + for(S32 index=0;indexmAnimations.size(); + if(numOfAnimations) + { + if(state->mRandom) + { + state->mCurrentAnimation=ll_frand()*numOfAnimations; + llwarns << "randomly chosen " << state->mCurrentAnimation << " of " << numOfAnimations << llendl; + } + else + { + state->mCurrentAnimation++; + if(state->mCurrentAnimation>=state->mAnimations.size()) + state->mCurrentAnimation=0; + llwarns << "cycle " << state->mCurrentAnimation << " of " << numOfAnimations << llendl; + } + return state->mAnimations[state->mCurrentAnimation].mAssetUUID; + } + else + llwarns << "animation state has no animations assigned" << llendl; + } + return LLUUID(); +} + +void AOSet::startTimer(F32 timeout) +{ + mEventTimer.stop(); + mPeriod=timeout; + mEventTimer.start(); + lldebugs << "Starting state timer for " << getName() << " at " << timeout << llendl; +} + +void AOSet::stopTimer() +{ + lldebugs << "State timer for " << getName() << " stopped." << llendl; + mEventTimer.stop(); +} + +BOOL AOSet::tick() +{ + AOEngine::instance().cycleTimeout(this); + return FALSE; +} + +const LLUUID AOSet::getInventoryUUID() const +{ + return mInventoryID; +} + +void AOSet::setInventoryUUID(const LLUUID inventoryID) +{ + mInventoryID=inventoryID; +} + +const std::string AOSet::getName() const +{ + return mName; +} + +void AOSet::setName(std::string name) +{ + mName=name; +} + +BOOL AOSet::getSitOverride() const +{ + return mSitOverride; +} + +void AOSet::setSitOverride(BOOL yes) +{ + mSitOverride=yes; +} + +BOOL AOSet::getMouselookDisable() const +{ + return mMouselookDisable; +} + +void AOSet::setMouselookDisable(BOOL yes) +{ + mMouselookDisable=yes; +} + +BOOL AOSet::getComplete() const +{ + return mComplete; +} + +void AOSet::setComplete(BOOL yes) +{ + mComplete=yes; +} + +BOOL AOSet::getDirty() const +{ + return mDirty; +} + +void AOSet::setDirty(BOOL yes) +{ + mDirty=yes; +} + +void AOSet::setMotion(const LLUUID motion) +{ + mCurrentMotion=motion; +} + +LLUUID AOSet::getMotion() const +{ + return mCurrentMotion; +} diff --git a/indra/newview/aoset.h b/indra/newview/aoset.h new file mode 100644 index 0000000000..aeae4a534c --- /dev/null +++ b/indra/newview/aoset.h @@ -0,0 +1,134 @@ +/** + * @file aoset.h + * @brief Implementation of an Animation Overrider set of animations + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Zi Ree @ Second Life + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AOSET_H +#define AOSET_H + +#include "llcommon.h" +#include "lleventtimer.h" + +class AOSet +: public LLEventTimer +{ + public: + AOSet(const LLUUID inventoryID); + ~AOSet(); + + // keep number and order in sync with list of names in the constructor + enum + { + Start=0, // convenience, so we don't have to know the name of the first state + Standing=0, + Walking, + Running, + Sitting, + SittingOnGround, + Crouching, + CrouchWalking, + Landing, + StandingUp, + Falling, + FlyingDown, + FlyingUp, + Flying, + FlyingSlow, + Hovering, + Jumping, + PreJumping, + TurningRight, + TurningLeft, + Typing, + Floating, + SwimmingForward, + SwimmingUp, + SwimmingDown, + AOSTATES_MAX + }; + + struct AOAnimation + { + std::string mName; + LLUUID mAssetUUID; + LLUUID mInventoryUUID; + S32 mSortOrder; + }; + + struct AOState + { + std::string mName; + LLUUID mRemapID; + BOOL mRandom; + S32 mCycleTime; + std::vector mAnimations; + S32 mCurrentAnimation; + LLUUID mCurrentAnimationID; + LLUUID mInventoryUUID; + BOOL mDirty; + }; + + const LLUUID getInventoryUUID() const; + void setInventoryUUID(const LLUUID inventoryID); + + const std::string getName() const; + void setName(std::string name); + + BOOL getSitOverride() const; + void setSitOverride(BOOL yes); + + BOOL getMouselookDisable() const; + void setMouselookDisable(BOOL yes); + + BOOL getComplete() const; + void setComplete(BOOL yes); + + LLUUID getMotion() const; + void setMotion(const LLUUID motion); + + BOOL getDirty() const; + void setDirty(BOOL yes); + + AOState* getState(S32 eName); + AOState* getStateByName(const std::string name); + AOState* getStateByRemapID(const LLUUID id); + LLUUID getAnimationForState(AOState* state); + + void startTimer(F32 timeout); + void stopTimer(); + virtual BOOL tick(); + + std::vector mStateNames; + + protected: + LLUUID mInventoryID; + + std::string mName; + BOOL mSitOverride; + BOOL mMouselookDisable; + BOOL mComplete; + LLUUID mCurrentMotion; + BOOL mDirty; + + AOState mStates[AOSTATES_MAX]; +}; + +#endif // AOSET_H diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index ae8307c324..debb3d1307 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -29,6 +29,7 @@ #include "pipeline.h" +#include "aoengine.h" // ## Zi: Animation Overrider #include "llagent.h" #include "llanimationstates.h" #include "llfloatercamera.h" @@ -2096,6 +2097,7 @@ void LLAgentCamera::changeCameraToMouselook(BOOL animate) updateLastCamera(); mCameraMode = CAMERA_MODE_MOUSELOOK; + AOEngine::getInstance()->inMouselook(TRUE); // ## Zi: Animation Overrider const U32 old_flags = gAgent.getControlFlags(); gAgent.setControlFlags(AGENT_CONTROL_MOUSELOOK); if (old_flags != gAgent.getControlFlags()) @@ -2157,6 +2159,7 @@ void LLAgentCamera::changeCameraToFollow(BOOL animate) updateLastCamera(); mCameraMode = CAMERA_MODE_FOLLOW; + AOEngine::getInstance()->inMouselook(FALSE); // ## Zi: Animation Overrider // bang-in the current focus, position, and up vector of the follow cam mFollowCam.reset(mCameraPositionAgent, LLViewerCamera::getInstance()->getPointOfInterest(), LLVector3::z_axis); @@ -2235,6 +2238,7 @@ void LLAgentCamera::changeCameraToThirdPerson(BOOL animate) } updateLastCamera(); mCameraMode = CAMERA_MODE_THIRD_PERSON; + AOEngine::getInstance()->inMouselook(FALSE); // ## Zi: Animation Overrider gAgent.clearControlFlags(AGENT_CONTROL_MOUSELOOK); } diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 2bbdeb04e8..5191c8ec0c 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -29,6 +29,7 @@ #include "llappviewer.h" // Viewer includes +#include "aoengine.h" // ## Zi: Animation Overrider #include "llversioninfo.h" #include "llfeaturemanager.h" #include "lluictrlfactory.h" @@ -1017,7 +1018,10 @@ bool LLAppViewer::init() LLAgentLanguage::init(); - + // ## Zi: Animation Overrider + setOnLoginCompletedCallback(boost::bind(&AOEngine::onLoginComplete)); + llwarns << "AO on login callback set up." << llendl; + // ## Zi: Animation Overrider return true; } diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp index ce584e2ba7..c6aaac2c72 100644 --- a/indra/newview/llbottomtray.cpp +++ b/indra/newview/llbottomtray.cpp @@ -38,6 +38,7 @@ #include "lltexteditor.h" // newview includes +#include "aoengine.h" // ## Zi: Animation Overrider #include "llagentcamera.h" #include "llchiclet.h" #include "llfloatercamera.h" @@ -539,6 +540,13 @@ void LLBottomTray::toggleCameraControls() mCamButton->onCommit(); } +// ## Zi: Animation Overrider +void LLBottomTray::toggleAO() +{ + AOEngine::getInstance()->enable(getChild("ao_toggle_btn")->getToggleState()); +} +// ## Zi: Animation Overrider + BOOL LLBottomTray::postBuild() { LLHints::registerHintTarget("bottom_tray", LLView::getHandle()); @@ -606,6 +614,12 @@ BOOL LLBottomTray::postBuild() getChild("sidebar_places_btn")->setCommitCallback(boost::bind(&LLBottomTray::showSidebarPanel, this, "panel_places")); getChild("sidebar_appearance_btn")->setCommitCallback(boost::bind(&LLBottomTray::showSidebarPanel, this, "sidepanel_appearance")); getChild("sidebar_inv_btn")->setCommitCallback(boost::bind(&LLBottomTray::showSidebarPanel, this, "sidepanel_inventory")); + // ## Zi: Animation Overrider + LLButton* aoToggleButton=getChild("ao_toggle_btn"); + aoToggleButton->setCommitCallback(boost::bind(&LLBottomTray::toggleAO,this)); + // this needs to become a preferences option + aoToggleButton->setToggleState(TRUE); + // ## Zi: Animation Overrider return TRUE; } diff --git a/indra/newview/llbottomtray.h b/indra/newview/llbottomtray.h index 553f40cd63..11da2d7962 100644 --- a/indra/newview/llbottomtray.h +++ b/indra/newview/llbottomtray.h @@ -124,6 +124,7 @@ public: void toggleMovementControls(); void toggleCameraControls(); + void toggleAO(); // ## Zi: Animation Overrider void onMouselookModeIn(); void onMouselookModeOut(); diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index f3a634e6b6..629c4ec6c9 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -31,6 +31,7 @@ #include "llviewerfloaterreg.h" +#include "ao.h" // ## Zi: Animation Overrider #include "kvfloaterflickrauth.h" #include "kvfloaterflickrupload.h" #include "llcompilequeue.h" @@ -141,6 +142,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterAboutUtil::registerFloater(); LLFloaterReg::add("about_land", "floater_about_land.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("animation_overrider", "floater_ao.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); // ## Zi: Animation Overrider LLFloaterReg::add("area_search", "floater_fs_area_search.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("auction", "floater_auction.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("avatar_picker", "floater_avatar_picker.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 11c8966575..e098988757 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -42,6 +42,7 @@ #include "noise.h" #include "sound_ids.h" +#include "aoengine.h" // ## Zi: Animation Overrider #include "llagent.h" // Get state values from here #include "llagentcamera.h" #include "llagentwearables.h" @@ -4794,7 +4795,19 @@ BOOL LLVOAvatar::startMotion(const LLUUID& id, F32 time_offset) lldebugs << "motion requested " << id.asString() << " " << gAnimLibrary.animationName(id) << llendl; - LLUUID remap_id = remapMotionID(id); + // ## Zi: Animation Overrider + LLUUID remap_id; + if(isSelf()) + { + remap_id=AOEngine::getInstance()->override(id,TRUE); + if(remap_id.isNull()) + remap_id=remapMotionID(id); + else + gAgent.sendAnimationRequest(remap_id,ANIM_REQUEST_START); + } + else + remap_id=remapMotionID(id); + // ## Zi: Animation Overrider if (remap_id != id) { @@ -4816,8 +4829,20 @@ BOOL LLVOAvatar::stopMotion(const LLUUID& id, BOOL stop_immediate) { lldebugs << "motion requested " << id.asString() << " " << gAnimLibrary.animationName(id) << llendl; - LLUUID remap_id = remapMotionID(id); - + // ## Zi: Animation Overrider + LLUUID remap_id; + if(isSelf()) + { + remap_id=AOEngine::getInstance()->override(id,FALSE); + if(remap_id.isNull()) + remap_id=remapMotionID(id); + else + gAgent.sendAnimationRequest(remap_id,ANIM_REQUEST_STOP); + } + else + remap_id=remapMotionID(id); + // ## Zi: Animation Overrider + if (remap_id != id) { lldebugs << "motion resultant " << remap_id.asString() << " " << gAnimLibrary.animationName(remap_id) << llendl; diff --git a/indra/newview/skins/default/xui/en/floater_ao.xml b/indra/newview/skins/default/xui/en/floater_ao.xml new file mode 100644 index 0000000000..c29cf4b23c --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_ao.xml @@ -0,0 +1,39 @@ + + + + + No Sets Loaded + + + + No Animations Loaded + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_bottomtray.xml b/indra/newview/skins/default/xui/en/menu_bottomtray.xml index 9c9d57f004..92a7883830 100644 --- a/indra/newview/skins/default/xui/en/menu_bottomtray.xml +++ b/indra/newview/skins/default/xui/en/menu_bottomtray.xml @@ -140,6 +140,17 @@ function="CheckControl" parameter="ShowPlacesButton" /> + + + + + + +Specify a name for the new AO set: +(The name may not contain a : character) +
+ +New AO Set + +