From b537e7cb9db18f73bbbe8391168382ea1aff0775 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Sun, 31 Dec 2023 00:01:01 -0500
Subject: [PATCH] Smash the ao back in

---
 indra/newview/CMakeLists.txt                  |   16 +-
 indra/newview/alaoengine.cpp                  | 2108 --------------
 indra/newview/alaoengine.h                    |  227 --
 indra/newview/alaoset.h                       |  152 -
 indra/newview/alchatcommand.cpp               |   12 +-
 indra/newview/alfloaterao.cpp                 |  740 -----
 indra/newview/alfloaterao.h                   |  146 -
 indra/newview/alpanelaomini.cpp               |   28 +-
 indra/newview/ao.cpp                          |  904 ++++++
 indra/newview/ao.h                            |  159 ++
 indra/newview/aoengine.cpp                    | 2446 +++++++++++++++++
 indra/newview/aoengine.h                      |  234 ++
 indra/newview/{alaoset.cpp => aoset.cpp}      |  206 +-
 indra/newview/aoset.h                         |  145 +
 indra/newview/app_settings/commands.xml       |   10 +
 .../settings_per_account_alchemy.xml          |  100 +-
 indra/newview/llagentcamera.cpp               |    8 +-
 indra/newview/llinventorybridge.cpp           |    5 +-
 indra/newview/llinventorymodel.cpp            |    7 +-
 indra/newview/llstatusbar.cpp                 |   90 +-
 indra/newview/llstatusbar.h                   |    6 +-
 indra/newview/llviewerfloaterreg.cpp          |    4 +-
 indra/newview/llvoavatar.cpp                  |   28 +-
 .../skins/default/xui/en/floater_ao.xml       |   72 +-
 .../newview/skins/default/xui/en/panel_ao.xml |  903 +++---
 .../skins/default/xui/en/panel_status_bar.xml |   11 +-
 26 files changed, 4769 insertions(+), 3998 deletions(-)
 delete mode 100644 indra/newview/alaoengine.cpp
 delete mode 100644 indra/newview/alaoengine.h
 delete mode 100644 indra/newview/alaoset.h
 delete mode 100644 indra/newview/alfloaterao.cpp
 delete mode 100644 indra/newview/alfloaterao.h
 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
 rename indra/newview/{alaoset.cpp => aoset.cpp} (52%)
 create mode 100644 indra/newview/aoset.h

diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 19fbcd57fab..94cf4419dec 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -84,19 +84,19 @@ endif()
 
 set(viewer_SOURCE_FILES
     alcinematicmode.cpp
-    #alaoengine.cpp
-    #alaoset.cpp
+    ao.cpp
+    aoengine.cpp
+    aoset.cpp
     alavataractions.cpp
     alavatargroups.cpp
     alchatcommand.cpp
     alcontrolcache.cpp
-    #alfloaterao.cpp
     alfloaterexploresounds.cpp
     alfloatergenerictext.cpp
     alfloaterlightbox.cpp
     alfloaterparticleeditor.cpp
     alfloaterregiontracker.cpp
-    #alpanelaomini.cpp
+    alpanelaomini.cpp
     alpanelaopulldown.cpp
     alpanelmusicticker.cpp
     alpanelradaralert.cpp
@@ -815,19 +815,19 @@ set(viewer_HEADER_FILES
     CMakeLists.txt
     ViewerInstall.cmake
     alcinematicmode.h
-    #alaoengine.h
-    #alaoset.h
+    ao.h
+    aoengine.h
+    aoset.h
     alavataractions.h
     alavatargroups.h
     alchatcommand.h
     alcontrolcache.h
-    #alfloaterao.h
     alfloaterexploresounds.h
     alfloatergenerictext.h
     alfloaterlightbox.h
     alfloaterparticleeditor.h
     alfloaterregiontracker.h
-    #alpanelaomini.h
+    alpanelaomini.h
     alpanelaopulldown.h
     alpanelmusicticker.h
     alpanelradaralert.h
diff --git a/indra/newview/alaoengine.cpp b/indra/newview/alaoengine.cpp
deleted file mode 100644
index 2b662c62367..00000000000
--- a/indra/newview/alaoengine.cpp
+++ /dev/null
@@ -1,2108 +0,0 @@
-/**
- * @file alaoengine.cpp
- * @brief The core Animation Overrider engine
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2011, Zi Ree @ Second Life
- * Copyright (C) 2020, Rye Mutt <rye@alchemyviewer.org>
- *
- * 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
- * $/LicenseInfo$
- */
-
-#include "llviewerprecompiledheaders.h"
-
-#include "roles_constants.h"
-
-#include "alaoengine.h"
-#include "alaoset.h"
-
-#include "llagent.h"
-#include "llagentcamera.h"
-#include "llanimationstates.h"
-#include "llassetstorage.h"
-#include "llfilesystem.h"
-#include "llinventorydefines.h"
-#include "llinventoryfunctions.h"
-#include "llinventorymodel.h"
-#include "llnotificationsutil.h"
-#include "llviewercontrol.h"
-#include "llviewerinventory.h"
-#include "llviewerobjectlist.h"
-#include "llvoavatarself.h"
-
-const F32 INVENTORY_POLLING_INTERVAL = 5.0f;
-
-const std::string ROOT_AO_FOLDER = LLStringExplicit("Animation Overrides");
-
-ALAOEngine::ALAOEngine()
-: mEnabled(false)
-, mInMouselook(false)
-, mUnderWater(false)
-, mAOFolder(LLUUID::null)
-, mLastMotion(ANIM_AGENT_STAND)
-, mLastOverriddenMotion(ANIM_AGENT_STAND)
-, mCurrentSet(nullptr)
-, mDefaultSet(nullptr)
-, mImportSet(nullptr)
-, mImportCategory(LLUUID::null)
-, mImportRetryCount(0)
-{
-	gSavedPerAccountSettings.getControl("AlchemyAOEnable")->getCommitSignal()->connect(boost::bind(&ALAOEngine::onToggleAOControl, this));
-
-	mRegionChangeConnection = gAgent.addRegionChangedCallback(boost::bind(&ALAOEngine::onRegionChange, this));
-}
-
-ALAOEngine::~ALAOEngine()
-{
-	clear(false);
-
-	if (mRegionChangeConnection.connected())
-	{
-		mRegionChangeConnection.disconnect();
-	}
-}
-
-void ALAOEngine::init()
-{
-	enable(gSavedPerAccountSettings.getBool("AlchemyAOEnable"));
-}
-
-// static
-void ALAOEngine::onLoginComplete()
-{
-	ALAOEngine::instance().init();
-}
-
-void ALAOEngine::onToggleAOControl()
-{
-	enable(gSavedPerAccountSettings.getBool("AlchemyAOEnable"));
-}
-
-void ALAOEngine::clear(bool aFromTimer)
-{
-	mOldSets.insert(mOldSets.end(), mSets.begin(), mSets.end());
-	mSets.clear();
-
-	mCurrentSet = nullptr;
-
-	if (!aFromTimer)
-	{
-		std::for_each(mOldSets.begin(), mOldSets.end(), DeletePointer());
-		mOldSets.clear();
-
-		std::for_each(mOldImportSets.begin(), mOldImportSets.end(), DeletePointer());
-		mOldImportSets.clear();
-	}
-}
-
-void ALAOEngine::stopAllStandVariants()
-{
-	LL_DEBUGS("AOEngine") << "stopping all STAND variants." << LL_ENDL;
-	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 ALAOEngine::stopAllSitVariants()
-{
-	LL_DEBUGS("AOEngine") << "stopping all SIT variants." << LL_ENDL;
-	gAgent.sendAnimationRequest(ANIM_AGENT_SIT_FEMALE, ANIM_REQUEST_STOP);
-	gAgent.sendAnimationRequest(ANIM_AGENT_SIT_GENERIC, ANIM_REQUEST_STOP);
-	gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_SIT_FEMALE);
-	gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_SIT_GENERIC);
-
-	// scripted seats that use ground_sit as animation need special treatment
-	const LLViewerObject* agentRoot = dynamic_cast<LLViewerObject*>(gAgentAvatarp->getRoot());
-	if (agentRoot && agentRoot->getID() != gAgentID)
-	{
-		LL_DEBUGS("AOEngine") << "Not stopping ground sit animations while sitting on a prim." << LL_ENDL;
-		return;
-	}
-
-	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_GROUND);
-	gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_SIT_GROUND_CONSTRAINED);
-}
-
-void ALAOEngine::setLastMotion(const LLUUID& motion)
-{
-	if (motion != ANIM_AGENT_TYPE)
-	{
-		mLastMotion = motion;
-	}
-}
-
-void ALAOEngine::setLastOverriddenMotion(const LLUUID& motion)
-{
-	if (motion != ANIM_AGENT_TYPE)
-	{
-		mLastOverriddenMotion = motion;
-	}
-}
-
-bool ALAOEngine::foreignAnimations(const LLUUID& seat)
-{
-	LL_DEBUGS("AOEngine") << "Checking for foreign animation on seat " << seat << LL_ENDL;
-
-	for (auto& animation_source : gAgentAvatarp->mAnimationSources)
-    {
-		LL_DEBUGS("AOEngine") << "Source " << animation_source.first << " runs animation " << animation_source.second << LL_ENDL;
-
-		if (animation_source.first != gAgentID)
-		{
-			// special case when the AO gets disabled while sitting
-			if (seat.isNull())
-			{
-				return true;
-			}
-
-			// find the source object where the animation came from
-			LLViewerObject* source = gObjectList.findObject(animation_source.first);
-
-			// proceed if it's not an attachment
-			if(source && !source->isAttachment())
-			{
-				// get the source's root prim
-				LLViewerObject* sourceRoot=dynamic_cast<LLViewerObject*>(source->getRoot());
-
-				// if the root prim is the same as the animation source, report back as TRUE
-				if (sourceRoot && source->getID() == seat)
-				{
-					LL_DEBUGS("AOEngine") << "foreign animation " << animation_source.second << " found on seat." << LL_ENDL;
-					return true;
-				}
-			}
-		}
-	}
-	return false;
-}
-
-const LLUUID& ALAOEngine::mapSwimming(const LLUUID& motion) const
-{
-	S32 name;
-
-	if (motion == ANIM_AGENT_HOVER)
-	{
-		name = ALAOSet::Floating;
-	}
-	else if (motion == ANIM_AGENT_FLY)
-	{
-		name = ALAOSet::SwimmingForward;
-	}
-	else if (motion == ANIM_AGENT_HOVER_UP)
-	{
-		name = ALAOSet::SwimmingUp;
-	}
-	else if (motion == ANIM_AGENT_HOVER_DOWN)
-	{
-		name = ALAOSet::SwimmingDown;
-	}
-	else
-	{
-		return LLUUID::null;
-	}
-
-	ALAOSet::AOState* state = mCurrentSet->getState(name);
-	return mCurrentSet->getAnimationForState(state);
-}
-
-void ALAOEngine::checkBelowWater(const bool under)
-{
-	if (mUnderWater == under) return;
-
-	// only restart underwater/above water motion if the overridden motion is the one currently playing
-	if (mLastMotion != mLastOverriddenMotion) return;
-
-	gAgent.sendAnimationRequest(override(mLastOverriddenMotion, false), ANIM_REQUEST_STOP);
-	mUnderWater = under;
-	gAgent.sendAnimationRequest(override(mLastOverriddenMotion, true), ANIM_REQUEST_START);
-}
-
-void ALAOEngine::enable(const bool enable)
-{
-	LL_DEBUGS("AOEngine") << "using " << mLastMotion << " enable " << enable << LL_ENDL;
-	mEnabled = enable;
-
-	if (!mCurrentSet)
-	{
-		LL_DEBUGS("AOEngine") << "enable(" << enable << ") without animation set loaded." << LL_ENDL;
-		return;
-	}
-
-	ALAOSet::AOState* state = mCurrentSet->getStateByRemapID(mLastMotion);
-	if (mEnabled)
-	{
-		if (state && !state->mAnimations.empty())
-		{
-			LL_DEBUGS("AOEngine") << "Enabling animation state " << state->mName << LL_ENDL;
-
-			// do not stop underlying ground sit when re-enabling the AO
-			if (mLastOverriddenMotion != ANIM_AGENT_SIT_GROUND_CONSTRAINED)
-			{
-				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)
-			{
-				LL_DEBUGS("AOEngine") << "Last motion was a WALK, stopping all variants." << LL_ENDL;
-				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)
-			{
-				LL_DEBUGS("AOEngine") << "Last motion was a RUN, stopping all variants." << LL_ENDL;
-				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();
-				gAgent.sendAnimationRequest(ANIM_AGENT_SIT_GENERIC, ANIM_REQUEST_START);
-			}
-			else
-			{
-				LL_WARNS("AOEngine") << "Unhandled last motion id " << mLastMotion << LL_ENDL;
-			}
-
-			gAgent.sendAnimationRequest(animation, ANIM_REQUEST_START);
-			mAnimationChangedSignal(state->mAnimations[state->mCurrentAnimation].mInventoryUUID);
-		}
-	}
-	else
-	{
-		mAnimationChangedSignal(LLUUID::null);
-
-		gAgent.sendAnimationRequest(ANIM_AGENT_SIT_GENERIC, ANIM_REQUEST_STOP);
-		// stop all overriders, catch leftovers
-		for (U32 index = 0; index < ALAOSet::AOSTATES_MAX; ++index)
-		{
-			state = mCurrentSet->getState(index);
-			if (state)
-			{
-				LLUUID animation = state->mCurrentAnimationID;
-				if (animation.notNull())
-				{
-					LL_DEBUGS("AOEngine") << "Stopping leftover animation from state " << state->mName << LL_ENDL;
-					gAgent.sendAnimationRequest(animation, ANIM_REQUEST_STOP);
-					gAgentAvatarp->LLCharacter::stopMotion(animation);
-					state->mCurrentAnimationID.setNull();
-				}
-			}
-			else
-			{
-				LL_DEBUGS("AOEngine") << "state "<< index <<" returned NULL." << LL_ENDL;
-			}
-		}
-
-		if (!foreignAnimations(LLUUID::null))
-		{
-			gAgent.sendAnimationRequest(mLastMotion, ANIM_REQUEST_START);
-		}
-
-		mCurrentSet->stopTimer();
-	}
-}
-
-void ALAOEngine::setStateCycleTimer(const ALAOSet::AOState* state)
-{
-	F32 timeout = state->mCycleTime;
-	LL_DEBUGS("AOEngine") << "Setting cycle timeout for state " << state->mName << " of " << timeout << LL_ENDL;
-	if (timeout > 0.0f)
-	{
-		mCurrentSet->startTimer(timeout);
-	}
-}
-
-const LLUUID ALAOEngine::override(const LLUUID& pMotion, const bool start)
-{
-#ifdef SHOW_DEBUG
-	LL_DEBUGS("AOEngine") << "override(" << pMotion << "," << start << ")" << LL_ENDL;
-#endif
-
-	LLUUID animation = LLUUID::null;
-
-	LLUUID motion = pMotion;
-
-	if (!mEnabled)
-	{
-		if (start && mCurrentSet)
-		{
-			const ALAOSet::AOState* state = mCurrentSet->getStateByRemapID(motion);
-			if (state)
-			{
-				setLastMotion(motion);
-#ifdef SHOW_DEBUG
-				LL_DEBUGS("AOEngine") << "(disabled AO) setting last motion id to " <<  gAnimLibrary.animationName(mLastMotion) << LL_ENDL;
-#endif
-				if (!state->mAnimations.empty())
-				{
-					setLastOverriddenMotion(motion);
-#ifdef SHOW_DEBUG
-					LL_DEBUGS("AOEngine") << "(disabled AO) setting last overridden motion id to " <<  gAnimLibrary.animationName(mLastOverriddenMotion) << LL_ENDL;
-#endif
-				}
-			}
-		}
-		return animation;
-	}
-
-	if (mSets.empty())
-	{
-#ifdef SHOW_DEBUG
-		LL_DEBUGS("AOEngine") << "No sets loaded. Skipping overrider." << LL_ENDL;
-#endif
-		return animation;
-	}
-
-	if (!mCurrentSet)
-	{
-#ifdef SHOW_DEBUG
-		LL_DEBUGS("AOEngine") << "No current AO set chosen. Skipping overrider." << LL_ENDL;
-#endif
-		return animation;
-	}
-
-	// we don't distinguish between these two
-	if (motion == ANIM_AGENT_SIT_GROUND)
-	{
-		motion = ANIM_AGENT_SIT_GROUND_CONSTRAINED;
-	}
-
-	ALAOSet::AOState* state = mCurrentSet->getStateByRemapID(motion);
-	if (!state)
-	{
-#ifdef SHOW_DEBUG
-		LL_DEBUGS("AOEngine") << "No current AO state for motion " << motion << " (" << gAnimLibrary.animationName(motion) << ")." << LL_ENDL;
-#endif
-		return animation;
-	}
-
-	mAnimationChangedSignal(LLUUID::null);
-
-	mCurrentSet->stopTimer();
-	if (start)
-	{
-		setLastMotion(motion);
-#ifdef SHOW_DEBUG
-		LL_DEBUGS("AOEngine") << "(enabled AO) setting last motion id to " <<  gAnimLibrary.animationName(mLastMotion) << LL_ENDL;
-#endif
-
-		// Disable start stands in Mouselook
-		if (mCurrentSet->getMouselookDisable() &&
-			motion == ANIM_AGENT_STAND &&
-			mInMouselook)
-		{
-#ifdef SHOW_DEBUG
-			LL_DEBUGS("AOEngine") << "(enabled AO, mouselook stand stopped) setting last motion id to " <<  gAnimLibrary.animationName(mLastMotion) << LL_ENDL;
-#endif
-			return animation;
-		}
-
-		// Do not start override sits if not selected
-		if (!mCurrentSet->getSitOverride() && motion == ANIM_AGENT_SIT)
-		{
-#ifdef SHOW_DEBUG
-			LL_DEBUGS("AOEngine") << "(enabled AO, sit override stopped) setting last motion id to " <<  gAnimLibrary.animationName(mLastMotion) << LL_ENDL;
-#endif
-			return animation;
-		}
-
-		// scripted seats that use ground_sit as animation need special treatment
-		if (motion == ANIM_AGENT_SIT_GROUND_CONSTRAINED)
-		{
-			const LLViewerObject* agentRoot = dynamic_cast<LLViewerObject*>(gAgentAvatarp->getRoot());
-			if (agentRoot && agentRoot->getID() != gAgentID)
-			{
-#ifdef SHOW_DEBUG
-				LL_DEBUGS("AOEngine") << "Ground sit animation playing but sitting on a prim - disabling overrider." << LL_ENDL;
-#endif
-				return animation;
-			}
-		}
-
-		if (!state->mAnimations.empty())
-		{
-			setLastOverriddenMotion(motion);
-#ifdef SHOW_DEBUG
-			LL_DEBUGS("AOEngine") << "(enabled AO) setting last overridden motion id to " <<  gAnimLibrary.animationName(mLastOverriddenMotion) << LL_ENDL;
-#endif
-		}
-
-		// do not remember typing as set-wide motion
-		if (motion != ANIM_AGENT_TYPE)
-		{
-			mCurrentSet->setMotion(motion);
-		}
-
-		mUnderWater = gAgentAvatarp->mBelowWater;
-		if (mUnderWater)
-		{
-			animation = mapSwimming(motion);
-		}
-
-		if (animation.isNull())
-		{
-			animation = mCurrentSet->getAnimationForState(state);
-		}
-
-		if (state->mCurrentAnimationID.notNull())
-		{
-#ifdef SHOW_DEBUG
-			LL_DEBUGS("AOEngine")	<< "Previous animation for state "
-						<< gAnimLibrary.animationName(motion)
-						<< " was not stopped, but we were asked to start a new one. Killing old animation." << LL_ENDL;
-#endif
-			gAgent.sendAnimationRequest(state->mCurrentAnimationID, ANIM_REQUEST_STOP);
-			gAgentAvatarp->LLCharacter::stopMotion(state->mCurrentAnimationID);
-		}
-
-		state->mCurrentAnimationID = animation;
-#ifdef SHOW_DEBUG
-		LL_DEBUGS("AOEngine")	<< "overriding " <<  gAnimLibrary.animationName(motion)
-					<< " with " << animation
-					<< " in state " << state->mName
-					<< " of set " << mCurrentSet->getName()
-					<< " (" << mCurrentSet << ")" << LL_ENDL;
-#endif
-
-		if (animation.notNull() && state->mCurrentAnimation < state->mAnimations.size())
-		{
-			mAnimationChangedSignal(state->mAnimations[state->mCurrentAnimation].mInventoryUUID);
-		}
-
-		setStateCycleTimer(state);
-
-		if (motion == ANIM_AGENT_SIT)
-		{
-			// Use ANIM_AGENT_SIT_GENERIC, so we don't create an overrider loop with ANIM_AGENT_SIT
-			// while still having a base sitting pose to cover up cycle points
-			gAgent.sendAnimationRequest(ANIM_AGENT_SIT_GENERIC, ANIM_REQUEST_START);
-			if (mCurrentSet->getSmart())
-			{
-				mSitCancelTimer.oneShot();
-			}
-		}
-		// special treatment for "transient animations" because the viewer needs the Linden animation to know the agent's state
-		else if (motion == ANIM_AGENT_SIT_GROUND_CONSTRAINED ||
-				motion == ANIM_AGENT_PRE_JUMP ||
-				motion == ANIM_AGENT_STANDUP ||
-				motion == ANIM_AGENT_LAND ||
-				motion == ANIM_AGENT_MEDIUM_LAND)
-		{
-			gAgent.sendAnimationRequest(animation, ANIM_REQUEST_START);
-			return LLUUID::null;
-		}
-	}
-	else
-	{
-		animation = state->mCurrentAnimationID;
-		state->mCurrentAnimationID.setNull();
-
-		// for typing animaiton, just return the stored animation, reset the state timer, and don't memorize anything else
-		if (motion == ANIM_AGENT_TYPE)
-		{
-			ALAOSet::AOState* previousState = mCurrentSet->getStateByRemapID(mLastMotion);
-			if (previousState)
-			{
-				setStateCycleTimer(previousState);
-			}
-			return animation;
-		}
-
-		if (motion != mCurrentSet->getMotion())
-		{
-			LL_WARNS("AOEngine") << "trying to stop-override motion " <<  gAnimLibrary.animationName(motion)
-					<< " but the current set has motion " <<  gAnimLibrary.animationName(mCurrentSet->getMotion()) << LL_ENDL;
-			return animation;
-		}
-
-		mCurrentSet->setMotion(LLUUID::null);
-
-		// again, special treatment for "transient" animations to make sure our own animation gets stopped properly
-		if (motion == ANIM_AGENT_SIT_GROUND_CONSTRAINED ||
-			motion == ANIM_AGENT_PRE_JUMP ||
-			motion == ANIM_AGENT_STANDUP ||
-			motion == ANIM_AGENT_LAND ||
-			motion == ANIM_AGENT_MEDIUM_LAND)
-		{
-			gAgent.sendAnimationRequest(animation, ANIM_REQUEST_STOP);
-			gAgentAvatarp->LLCharacter::stopMotion(animation);
-			setStateCycleTimer(state);
-			return 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();
-		}
-#ifdef SHOW_DEBUG
-		LL_DEBUGS("AOEngine") << "stopping cycle timer for motion " <<  gAnimLibrary.animationName(motion) <<
-					" using animation " << animation <<
-					" in state " << state->mName << LL_ENDL;
-#endif
-	}
-
-	return animation;
-}
-
-void ALAOEngine::checkSitCancel()
-{
-	LLUUID seat;
-
-	const LLViewerObject* agentRoot = dynamic_cast<LLViewerObject*>(gAgentAvatarp->getRoot());
-	if (agentRoot)
-	{
-		seat = agentRoot->getID();
-	}
-
-	if (foreignAnimations(seat))
-	{
-		ALAOSet::AOState* aoState = mCurrentSet->getStateByRemapID(ANIM_AGENT_SIT);
-		if (aoState)
-		{
-			LLUUID animation = aoState->mCurrentAnimationID;
-			if (animation.notNull())
-			{
-				LL_DEBUGS("AOEngine") << "Stopping sit animation due to foreign animations running" << LL_ENDL;
-				gAgent.sendAnimationRequest(animation, ANIM_REQUEST_STOP);
-				// remove cycle point cover-up
-				gAgent.sendAnimationRequest(ANIM_AGENT_SIT_GENERIC, ANIM_REQUEST_STOP);
-				gAgentAvatarp->LLCharacter::stopMotion(animation);
-				mSitCancelTimer.stop();
-				// stop cycle tiemr
-				mCurrentSet->stopTimer();
-			}
-		}
-	}
-}
-
-void ALAOEngine::cycleTimeout(const ALAOSet* set)
-{
-	if (!mEnabled)
-	{
-		return;
-	}
-
-	if (set != mCurrentSet)
-	{
-		LL_WARNS("AOEngine") << "cycleTimeout for set " << set->getName() << " but current set is " << mCurrentSet->getName() << LL_ENDL;
-		return;
-	}
-
-	cycle(CycleAny);
-}
-
-void ALAOEngine::cycle(eCycleMode cycleMode)
-{
-	if (!mCurrentSet)
-	{
-		LL_DEBUGS("AOEngine") << "cycle without set." << LL_ENDL;
-		return;
-	}
-
-	LLUUID motion = mCurrentSet->getMotion();
-
-	// assume stand if no motion is registered, happens after login when the avatar hasn't moved at all yet
-	// or if the agent has said something in local chat while sitting
-	if (motion.isNull())
-	{
-		if (gAgentAvatarp->isSitting())
-		{
-			motion = ANIM_AGENT_SIT;
-		}
-		else
-		{
-			motion = ANIM_AGENT_STAND;
-		}
-	}
-
-	// do not cycle if we're sitting and sit-override is off
-	else if (motion == ANIM_AGENT_SIT && !mCurrentSet->getSitOverride())
-	{
-		return;
-	}
-	// do not cycle if we're standing and mouselook stand override is disabled while being in mouselook
-	else if (motion == ANIM_AGENT_STAND && mCurrentSet->getMouselookDisable() && mInMouselook)
-	{
-		return;
-	}
-
-	ALAOSet::AOState* state = mCurrentSet->getStateByRemapID(motion);
-	if (!state)
-	{
-		LL_DEBUGS("AOEngine") << "cycle without state." << LL_ENDL;
-		return;
-	}
-
-	if (state->mAnimations.empty())
-	{
-		LL_DEBUGS("AOEngine") << "cycle without animations in state." << LL_ENDL;
-		return;
-	}
-
-	// make sure we disable cycling only for timed cycle, so manual cycling still works, even with cycling switched off
-	if (!state->mCycle && cycleMode == CycleAny)
-	{
-		LL_DEBUGS("AOEngine") << "cycle timeout, but state is set to not cycling." << LL_ENDL;
-		return;
-	}
-
-	LLUUID oldAnimation = state->mCurrentAnimationID;
-	LLUUID animation;
-
-	if (cycleMode == CycleAny)
-	{
-		animation = mCurrentSet->getAnimationForState(state);
-	}
-	else
-	{
-		if (cycleMode == CyclePrevious)
-		{
-			if (state->mCurrentAnimation == 0)
-			{
-				state->mCurrentAnimation = state->mAnimations.size() - 1;
-			}
-			else
-			{
-				state->mCurrentAnimation--;
-			}
-		}
-		else if (cycleMode == CycleNext)
-		{
-			state->mCurrentAnimation++;
-			if (state->mCurrentAnimation == state->mAnimations.size())
-			{
-				state->mCurrentAnimation = 0;
-			}
-		}
-		animation = state->mAnimations[state->mCurrentAnimation].mAssetUUID;
-	}
-
-	// don't do anything if the animation didn't change
-	if (animation == oldAnimation)
-	{
-		return;
-	}
-
-	mAnimationChangedSignal(LLUUID::null);
-
-	state->mCurrentAnimationID = animation;
-	if (animation.notNull())
-	{
-		LL_DEBUGS("AOEngine") << "requesting animation start for motion " << gAnimLibrary.animationName(motion) << ": " << animation << LL_ENDL;
-		gAgent.sendAnimationRequest(animation, ANIM_REQUEST_START);
-		mAnimationChangedSignal(state->mAnimations[state->mCurrentAnimation].mInventoryUUID);
-	}
-	else
-	{
-		LL_DEBUGS("AOEngine") << "overrider came back with NULL animation for motion " << gAnimLibrary.animationName(motion) << "." << LL_ENDL;
-	}
-
-	if (oldAnimation.notNull())
-	{
-		LL_DEBUGS("AOEngine") << "Cycling state " << state->mName << " - stopping animation " << oldAnimation << LL_ENDL;
-		gAgent.sendAnimationRequest(oldAnimation, ANIM_REQUEST_STOP);
-		gAgentAvatarp->LLCharacter::stopMotion(oldAnimation);
-	}
-}
-
-void ALAOEngine::updateSortOrder(ALAOSet::AOState* state)
-{
-	for (U32 index = 0; index < state->mAnimations.size(); ++index)
-	{
-		auto& anim = state->mAnimations[index];
-		U32 sortOrder = anim.mSortOrder;
-
-		if (sortOrder != index)
-		{
-			std::ostringstream numStr("");
-			numStr << index;
-
-			LL_DEBUGS("AOEngine")	<< "sort order is " << sortOrder << " but index is " << index
-						<< ", setting sort order description: " << numStr.str() << LL_ENDL;
-
-			anim.mSortOrder = index;
-
-			LLViewerInventoryItem* item = gInventory.getItem(anim.mInventoryUUID);
-			if (!item)
-			{
-				LL_WARNS("AOEngine") << "NULL inventory item found while trying to copy " << anim.mInventoryUUID << LL_ENDL;
-				continue;
-			}
-			LLPointer<LLViewerInventoryItem> newItem = new LLViewerInventoryItem(item);
-
-			newItem->setDescription(numStr.str());
-			newItem->setComplete(TRUE);
-			newItem->updateServer(FALSE);
-
-			gInventory.updateItem(newItem);
-		}
-	}
-}
-
-void ALAOEngine::addSet(const std::string& name, const bool reload, inventory_func_type callback)
-{
-	if (mAOFolder.isNull())
-	{
-		LL_WARNS("AOEngine") << ROOT_AO_FOLDER << " folder not there yet. Requesting recreation." << LL_ENDL;
-		tick();
-		return;
-	}
-
-	LL_DEBUGS("AOEngine") << "adding set folder " << name << LL_ENDL;
-	gInventory.createNewCategory(mAOFolder, LLFolderType::FT_NONE, name, callback);
-
-	if (reload)
-	{
-		mTimerCollection.setReloadTimer(true);
-	}
-}
-
-bool ALAOEngine::createAnimationLink(const ALAOSet* set, ALAOSet::AOState* state, const LLInventoryItem* item)
-{
-	LL_DEBUGS("AOEngine") << "Asset ID " << item->getAssetUUID() << " inventory id "
-		<< item->getUUID() << " category id " << state->mInventoryUUID << LL_ENDL;
-	if (state->mInventoryUUID.isNull())
-	{
-		LL_DEBUGS("AOEngine") << "no " << state->mName << " folder yet. Creating ..." << LL_ENDL;
-		gInventory.createNewCategory(set->getInventoryUUID(), LLFolderType::FT_NONE, state->mName);
-
-		LL_DEBUGS("AOEngine") << "looking for folder to get UUID ..." << LL_ENDL;
-
-        LLInventoryModel::item_array_t* items;
-		LLInventoryModel::cat_array_t* cats;
-		gInventory.getDirectDescendentsOf(set->getInventoryUUID(), cats, items);
-
-		if (cats)
-		{
-			for (auto& cat : *cats)
-            {
-				if (cat->getName() == state->mName)
-				{
-					LL_DEBUGS("AOEngine") << "UUID found!" << LL_ENDL;
-					state->mInventoryUUID = cat->getUUID();
-					break;
-				}
-			}
-		}
-	}
-
-	if (state->mInventoryUUID.isNull())
-	{
-		LL_DEBUGS("AOEngine") << "state inventory UUID not found, failing." << LL_ENDL;
-		return FALSE;
-	}
-
-	LLInventoryObject::const_object_list_t obj_array;
-	obj_array.emplace_back(LLConstPointer<LLInventoryObject>(item));
-	link_inventory_array(state->mInventoryUUID,
-							obj_array,
-							LLPointer<LLInventoryCallback>(NULL));
-
-	return TRUE;
-}
-
-bool ALAOEngine::addAnimation(const ALAOSet* set, ALAOSet::AOState* state,
-							  const LLInventoryItem* item, const bool reload)
-{
-	state->mAnimations.emplace_back(item->getName(), item->getAssetUUID(), item->getUUID(), state->mAnimations.size() + 1);
-
-	createAnimationLink(set, state, item);
-
-	if (reload)
-	{
-		mTimerCollection.setReloadTimer(true);
-	}
-	return TRUE;
-}
-
-bool ALAOEngine::findForeignItems(const LLUUID& uuid) const
-{
-	bool moved = false;
-
-	LLInventoryModel::item_array_t* items;
-	LLInventoryModel::cat_array_t* cats;
-
-	gInventory.getDirectDescendentsOf(uuid, cats, items);
-	if (cats)
-	{
-		for (auto& cat : *cats)
-		{
-			// recurse into subfolders
-			if (findForeignItems(cat->getUUID()))
-			{
-				moved = true;
-			}
-		}
-	}
-
-	// count backwards in case we have to remove items
-	if (items)
-	{
-		for (S32 index = items->size() - 1; index >= 0; --index)
-		{
-			bool move = false;
-
-			LLPointer<LLViewerInventoryItem> item = items->at(index);
-			if (item->getIsLinkType())
-			{
-				if (item->getInventoryType() != LLInventoryType::IT_ANIMATION)
-				{
-#ifdef SHOW_DEBUG
-					LL_DEBUGS("AOEngine") << item->getName() << " is a link but does not point to an animation." << LL_ENDL;
-#endif
-					move = true;
-				}
-#ifdef SHOW_DEBUG
-				else
-				{
-					LL_DEBUGS("AOEngine") << item->getName() << " is an animation link." << LL_ENDL;
-				}
-#endif
-			}
-			else
-			{
-#ifdef SHOW_DEBUG
-				LL_DEBUGS("AOEngine") << item->getName() << " is not a link!" << LL_ENDL;
-#endif
-				move = true;
-			}
-
-			if (move)
-			{
-				moved = true;
-				LLInventoryModel* model = &gInventory;
-				model->changeItemParent(item, gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND), FALSE);
-#ifdef SHOW_DEBUG
-				LL_DEBUGS("AOEngine") << item->getName() << " moved to lost and found!" << LL_ENDL;
-#endif
-			}
-		}
-	}
-
-	return moved;
-}
-
-// needs a three-step process, since purge of categories only seems to work from trash
-void ALAOEngine::purgeFolder(const LLUUID& uuid) const
-{
-	// move everything that's not an animation link to "lost and found"
-	if (findForeignItems(uuid))
-	{
-		LLNotificationsUtil::add("AOForeignItemsFound", LLSD());
-	}
-
-	// trash it
-	gInventory.removeCategory(uuid);
-
-	// clean it
-	purge_descendents_of(uuid, NULL);
-	gInventory.notifyObservers();
-
-	// purge it
-	remove_inventory_object(uuid, NULL);
-	gInventory.notifyObservers();
-}
-
-bool ALAOEngine::removeSet(ALAOSet* set)
-{
-	purgeFolder(set->getInventoryUUID());
-
-	mTimerCollection.setReloadTimer(true);
-	return true;
-}
-
-bool ALAOEngine::removeAnimation(const ALAOSet* set, ALAOSet::AOState* state, S32 index)
-{
-	// Protect against negative index
-	if (index <= -1) return false;
-
-	size_t numOfAnimations = state->mAnimations.size();
-	if (!numOfAnimations) return false;
-
-	LLViewerInventoryItem* item = gInventory.getItem(state->mAnimations[index].mInventoryUUID);
-
-	// check if this item is actually an animation link
-	bool move = (item->getIsLinkType() && item->getInventoryType() == LLInventoryType::IT_ANIMATION) ? false : true;
-
-	// this item was not an animation link, move it to lost and found
-	if (move)
-	{
-		LLInventoryModel* model = &gInventory;
-		model->changeItemParent(item, gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND), false);
-		LLNotificationsUtil::add("AOForeignItemsFound", LLSD());
-		update();
-		return false;
-	}
-
-	// purge the item from inventory
-	LL_DEBUGS("AOEngine") << __LINE__ << " purging: " << state->mAnimations[index].mInventoryUUID << LL_ENDL;
-	remove_inventory_object(state->mAnimations[index].mInventoryUUID, NULL); // item->getUUID());
-	gInventory.notifyObservers();
-
-	state->mAnimations.erase(state->mAnimations.begin() + index);
-
-	if (state->mAnimations.empty())
-	{
-		LL_DEBUGS("AOEngine") << "purging folder " << state->mName << " from inventory because it's empty." << LL_ENDL;
-
-		LLInventoryModel::item_array_t* items;
-		LLInventoryModel::cat_array_t* cats;
-		gInventory.getDirectDescendentsOf(set->getInventoryUUID(), cats, items);
-
-		for (auto& it : *cats)
-        {
-			LLPointer<LLInventoryCategory> cat = it;
-			std::vector<std::string> params;
-			LLStringUtil::getTokens(cat->getName(), params, ":");
-			std::string stateName = params[0];
-
-			if (state->mName.compare(stateName) == 0)
-			{
-				LL_DEBUGS("AOEngine") << "folder found: " << cat->getName() << " purging uuid " << cat->getUUID() << LL_ENDL;
-
-				purgeFolder(cat->getUUID());
-				state->mInventoryUUID.setNull();
-				break;
-			}
-		}
-	}
-	else
-	{
-		updateSortOrder(state);
-	}
-
-	return true;
-}
-
-bool ALAOEngine::swapWithPrevious(ALAOSet::AOState* state, S32 index)
-{
-	S32 numOfAnimations = state->mAnimations.size();
-	if (numOfAnimations < 2 || index == 0)
-	{
-		return false;
-	}
-
-	ALAOSet::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 ALAOEngine::swapWithNext(ALAOSet::AOState* state, S32 index)
-{
-	S32 numOfAnimations = state->mAnimations.size();
-	if (numOfAnimations < 2 || index == (numOfAnimations - 1))
-	{
-		return false;
-	}
-
-	ALAOSet::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 ALAOEngine::reloadStateAnimations(ALAOSet::AOState* state)
-{
-	LLInventoryModel::item_array_t* items;
-	LLInventoryModel::cat_array_t* dummy;
-
-	state->mAnimations.clear();
-
-	gInventory.getDirectDescendentsOf(state->mInventoryUUID, dummy, items);
-	for (auto& item : *items)
-    {
-#ifdef SHOW_DEBUG
-		LL_DEBUGS("AOEngine")	<< "Found animation link " << item->LLInventoryItem::getName()
-					<< " desc " << item->LLInventoryItem::getDescription()
-					<< " asset " << item->getAssetUUID() << LL_ENDL;
-#endif
-
-		LLViewerInventoryItem* linkedItem = item->getLinkedItem();
-		if (!linkedItem)
-		{
-			LL_WARNS("AOEngine") << "linked item for link " << item->LLInventoryItem::getName() << " not found (broken link). Skipping." << LL_ENDL;
-			continue;
-		}
-
-		S32 sortOrder;
-		if (!LLStringUtil::convertToS32(item->LLInventoryItem::getDescription(), sortOrder))
-		{
-			sortOrder = -1;
-		}
-
-#ifdef SHOW_DEBUG
-		LL_DEBUGS("AOEngine") << "current sort order is " << sortOrder << LL_ENDL;
-#endif
-
-		if (sortOrder == -1)
-		{
-			LL_WARNS("AOEngine") << "sort order was unknown so append to the end of the list" << LL_ENDL;
-			state->mAnimations.emplace_back(linkedItem->LLInventoryItem::getName(), item->getAssetUUID(), item->getUUID(), sortOrder);
-		}
-		else
-		{
-			bool inserted = false;
-			for (U32 index = 0; index < state->mAnimations.size(); ++index)
-			{
-				if (state->mAnimations[index].mSortOrder > sortOrder)
-				{
-#ifdef SHOW_DEBUG
-					LL_DEBUGS("AOEngine") << "inserting at index " << index << LL_ENDL;
-#endif
-					state->mAnimations.emplace(state->mAnimations.begin() + index, 
-						linkedItem->LLInventoryItem::getName(), item->getAssetUUID(), item->getUUID(), sortOrder);
-					inserted = true;
-					break;
-				}
-			}
-			if (!inserted)
-			{
-#ifdef SHOW_DEBUG
-				LL_DEBUGS("AOEngine") << "not inserted yet, appending to the list instead" << LL_ENDL;
-#endif
-				state->mAnimations.emplace_back(linkedItem->LLInventoryItem::getName(), item->getAssetUUID(), item->getUUID(), sortOrder);
-			}
-		}
-#ifdef SHOW_DEBUG
-		LL_DEBUGS("AOEngine") << "Animation count now: " << state->mAnimations.size() << LL_ENDL;
-#endif
-	}
-
-	updateSortOrder(state);
-}
-
-void ALAOEngine::update()
-{
-	if (mAOFolder.isNull()) return;
-
-	// move everything that's not an animation link to "lost and found"
-	if (findForeignItems(mAOFolder))
-	{
-		LLNotificationsUtil::add("AOForeignItemsFound", LLSD());
-	}
-
-	LLInventoryModel::cat_array_t* categories;
-	LLInventoryModel::item_array_t* items;
-
-	bool allComplete = true;
-	mTimerCollection.setSettingsTimer(false);
-
-	gInventory.getDirectDescendentsOf(mAOFolder, categories, items);
-	for (auto& categorie : *categories)
-    {
-		LLViewerInventoryCategory* currentCategory = categorie;
-		const std::string& setFolderName = currentCategory->getName();
-
-		if (setFolderName.empty())
-		{
-			LL_WARNS("AOEngine") << "Folder with emtpy name in AO folder" << LL_ENDL;
-			continue;
-		}
-
-		std::vector<std::string> params;
-		LLStringUtil::getTokens(setFolderName, params, ":");
-
-		ALAOSet* newSet = getSetByName(params[0]);
-		if (!newSet)
-		{
-			LL_DEBUGS("AOEngine") << "Adding set " << setFolderName << " to AO." << LL_ENDL;
-			newSet = new ALAOSet(currentCategory->getUUID());
-			newSet->setName(params[0]);
-			mSets.emplace_back(newSet);
-		}
-		else
-		{
-			if (newSet->getComplete())
-			{
-				LL_DEBUGS("AOEngine") << "Set " << params[0] << " already complete. Skipping." << LL_ENDL;
-				continue;
-			}
-			LL_DEBUGS("AOEngine") << "Updating set " << setFolderName << " in AO." << LL_ENDL;
-		}
-		allComplete = FALSE;
-
-		for (U32 num = 1; num < params.size(); ++num)
-		{
-			if (params[num].size() != 2)
-			{
-				LL_WARNS("AOEngine") << "Unknown AO set option " << params[num] << LL_ENDL;
-			}
-			else if (params[num] == "SO")
-			{
-				newSet->setSitOverride(TRUE);
-			}
-			else if (params[num] == "SM")
-			{
-				newSet->setSmart(TRUE);
-			}
-			else if (params[num] == "DM")
-			{
-				newSet->setMouselookDisable(TRUE);
-			}
-			else if (params[num] == "**")
-			{
-				mDefaultSet = newSet;
-				mCurrentSet = newSet;
-			}
-			else
-			{
-				LL_WARNS("AOEngine") << "Unknown AO set option " << params[num] << LL_ENDL;
-			}
-		}
-
-		if (gInventory.isCategoryComplete(currentCategory->getUUID()))
-		{
-			LL_DEBUGS("AOEngine") << "Set " << params[0] << " is complete, reading states ..." << LL_ENDL;
-
-			LLInventoryModel::cat_array_t* stateCategories;
-			gInventory.getDirectDescendentsOf(currentCategory->getUUID(), stateCategories, items);
-			newSet->setComplete(TRUE);
-
-			for (auto& stateCategorie : *stateCategories)
-            {
-				std::vector<std::string> state_params;
-				LLStringUtil::getTokens(stateCategorie->getName(), state_params, ":");
-				std::string stateName = state_params[0];
-
-				ALAOSet::AOState* state = newSet->getStateByName(stateName);
-				if (!state)
-				{
-					LL_WARNS("AOEngine") << "Unknown state " << stateName << ". Skipping." << LL_ENDL;
-					continue;
-				}
-				LL_DEBUGS("AOEngine") << "Reading state " << stateName << LL_ENDL;
-
-				state->mInventoryUUID = stateCategorie->getUUID();
-				for (U32 num = 1; num < state_params.size(); ++num)
-				{
-					if (state_params[num] == "CY")
-					{
-						state->mCycle = TRUE;
-						LL_DEBUGS("AOEngine") << "Cycle on" << LL_ENDL;
-					}
-					else if (state_params[num] == "RN")
-					{
-						state->mRandom = TRUE;
-						LL_DEBUGS("AOEngine") << "Random on" << LL_ENDL;
-					}
-					else if (state_params[num].substr(0, 2) == "CT")
-					{
-						LLStringUtil::convertToS32(state_params[num].substr(2, state_params[num].size() - 2), state->mCycleTime);
-						LL_DEBUGS("AOEngine") << "Cycle Time specified:" << state->mCycleTime << LL_ENDL;
-					}
-					else
-					{
-						LL_WARNS("AOEngine") << "Unknown AO set option " << state_params[num] << LL_ENDL;
-					}
-				}
-
-				if (!gInventory.isCategoryComplete(state->mInventoryUUID))
-				{
-					LL_DEBUGS("AOEngine") << "State category " << stateName << " is incomplete, fetching descendents" << LL_ENDL;
-					gInventory.fetchDescendentsOf(state->mInventoryUUID);
-					allComplete = FALSE;
-					newSet->setComplete(FALSE);
-					continue;
-				}
-				reloadStateAnimations(state);
-			}
-		}
-		else
-		{
-			LL_DEBUGS("AOEngine") << "Set " << params[0] << " is incomplete, fetching descendents" << LL_ENDL;
-			gInventory.fetchDescendentsOf(currentCategory->getUUID());
-		}
-	}
-
-	if (allComplete)
-	{
-		mEnabled = gSavedPerAccountSettings.getBOOL("AlchemyAOEnable");
-
-		if (!mCurrentSet && !mSets.empty())
-		{
-			LL_DEBUGS("AOEngine") << "No default set defined, choosing the first in the list." << LL_ENDL;
-			selectSet(mSets[0]);
-		}
-
-		mTimerCollection.setInventoryTimer(false);
-		mTimerCollection.setSettingsTimer(true);
-
-		LL_INFOS("AOEngine") << "sending update signal" << LL_ENDL;
-		mUpdatedSignal();
-		enable(mEnabled);
-	}
-}
-
-void ALAOEngine::reload(bool aFromTimer)
-{
-	BOOL wasEnabled = mEnabled;
-
-	mTimerCollection.setReloadTimer(false);
-
-	if (wasEnabled)
-	{
-		enable(false);
-	}
-
-	gAgent.stopCurrentAnimations();
-	mLastOverriddenMotion = ANIM_AGENT_STAND;
-
-	clear(aFromTimer);
-	mAOFolder.setNull();
-	mTimerCollection.setInventoryTimer(true);
-	tick();
-
-	if (wasEnabled)
-	{
-		enable(false);
-	}
-}
-
-ALAOSet* ALAOEngine::getSetByName(const std::string& name) const
-{
-	ALAOSet* found = NULL;
-	for (auto set : mSets)
-    {
-		if (set->getName() == name)
-		{
-			found = set;
-			break;
-		}
-	}
-	return found;
-}
-
-const std::string& ALAOEngine::getCurrentSetName() const
-{
-	if(mCurrentSet)
-	{
-		return mCurrentSet->getName();
-	}
-	return LLStringUtil::null;
-}
-
-const ALAOSet* ALAOEngine::getDefaultSet() const
-{
-	return mDefaultSet;
-}
-
-void ALAOEngine::selectSet(ALAOSet* set)
-{
-	if (mEnabled && mCurrentSet)
-	{
-		ALAOSet::AOState* state = mCurrentSet->getStateByRemapID(mLastOverriddenMotion);
-		if (state)
-		{
-			gAgent.sendAnimationRequest(state->mCurrentAnimationID, ANIM_REQUEST_STOP);
-			state->mCurrentAnimationID.setNull();
-			mCurrentSet->stopTimer();
-		}
-	}
-
-	mCurrentSet = set;
-	if(mCurrentSet)
-	{
-		mSetChangedSignal(mCurrentSet->getName());
-	}
-
-	if (mEnabled)
-	{
-		LL_DEBUGS("AOEngine") << "enabling with motion " << gAnimLibrary.animationName(mLastMotion) << LL_ENDL;
-		gAgent.sendAnimationRequest(override(mLastMotion, TRUE), ANIM_REQUEST_START);
-	}
-}
-
-ALAOSet* ALAOEngine::selectSetByName(const std::string& name)
-{
-	ALAOSet* set = getSetByName(name);
-	if (set)
-	{
-		selectSet(set);
-		return set;
-	}
-	LL_WARNS("AOEngine") << "Could not find AO set " << name << LL_ENDL;
-	return nullptr;
-}
-
-const std::vector<ALAOSet*>& ALAOEngine::getSetList() const
-{
-	return mSets;
-}
-
-void ALAOEngine::saveSet(const ALAOSet* set)
-{
-	if (!set) return;
-
-	std::string setParams=set->getName();
-	if (set->getSitOverride())
-	{
-		setParams += ":SO";
-	}
-	if (set->getSmart())
-	{
-		setParams += ":SM";
-	}
-	if (set->getMouselookDisable())
-	{
-		setParams += ":DM";
-	}
-	if (set == mDefaultSet)
-	{
-		setParams += ":**";
-	}
-
-	rename_category(&gInventory, set->getInventoryUUID(), setParams);
-
-	LL_INFOS("AOEngine") << "sending update signal" << LL_ENDL;
-	mUpdatedSignal();
-}
-
-bool ALAOEngine::renameSet(ALAOSet* set, const std::string& name)
-{
-	if (name.empty() || name.find(':') != std::string::npos)
-	{
-		return false;
-	}
-	set->setName(name);
-	set->setDirty(true);
-
-	return true;
-}
-
-void ALAOEngine::saveState(const ALAOSet::AOState* state)
-{
-	std::string stateParams = state->mName;
-	F32 time = state->mCycleTime;
-	if (time > 0.0f)
-	{
-		std::ostringstream timeStr;
-		timeStr << ":CT" << state->mCycleTime;
-		stateParams += timeStr.str();
-	}
-	if (state->mCycle)
-	{
-		stateParams += ":CY";
-	}
-	if (state->mRandom)
-	{
-		stateParams += ":RN";
-	}
-
-	rename_category(&gInventory, state->mInventoryUUID, stateParams);
-}
-
-void ALAOEngine::saveSettings()
-{
-	for (auto set : mSets)
-    {
-        if (set->getDirty())
-		{
-			saveSet(set);
-			LL_INFOS("AOEngine") << "dirty set saved " << set->getName() << LL_ENDL;
-			set->setDirty(FALSE);
-		}
-
-		for (S32 stateIndex = 0; stateIndex < ALAOSet::AOSTATES_MAX; ++stateIndex)
-		{
-			ALAOSet::AOState* state = set->getState(stateIndex);
-			if (state->mDirty)
-			{
-				saveState(state);
-				LL_INFOS("AOEngine") << "dirty state saved " << state->mName << LL_ENDL;
-				state->mDirty = false;
-			}
-		}
-	}
-}
-
-void ALAOEngine::inMouselook(const bool in_mouselook)
-{
-	if (mInMouselook == in_mouselook) return;
-
-	mInMouselook = in_mouselook;
-
-	if (!mCurrentSet || !mCurrentSet->getMouselookDisable())
-	{
-		return;
-	}
-
-	if (!mEnabled)
-	{
-		return;
-	}
-
-	if (mLastMotion != ANIM_AGENT_STAND)
-	{
-		return;
-	}
-
-	if (in_mouselook)
-	{
-		ALAOSet::AOState* state = mCurrentSet->getState(ALAOSet::Standing);
-		if (!state)
-		{
-			return;
-		}
-
-		LLUUID animation = state->mCurrentAnimationID;
-		if (animation.notNull())
-		{
-			gAgent.sendAnimationRequest(animation, ANIM_REQUEST_STOP);
-			gAgentAvatarp->LLCharacter::stopMotion(animation);
-			state->mCurrentAnimationID.setNull();
-			LL_DEBUGS("AOEngine") << " stopped animation " << animation << " in state " << state->mName << LL_ENDL;
-		}
-		gAgent.sendAnimationRequest(ANIM_AGENT_STAND, ANIM_REQUEST_START);
-	}
-	else
-	{
-		stopAllStandVariants();
-		gAgent.sendAnimationRequest(override(ANIM_AGENT_STAND, TRUE), ANIM_REQUEST_START);
-	}
-}
-
-void ALAOEngine::setDefaultSet(ALAOSet* set)
-{
-	mDefaultSet = set;
-	for (auto& ao_set : mSets)
-    {
-        ao_set->setDirty(TRUE);
-	}
-}
-
-void ALAOEngine::setOverrideSits(ALAOSet* set, const bool yes)
-{
-	set->setSitOverride(yes);
-	set->setDirty(TRUE);
-
-	if (mCurrentSet != set)
-	{
-		return;
-	}
-
-	if (mLastMotion != ANIM_AGENT_SIT)
-	{
-		return;
-	}
-
-	if (yes)
-	{
-		stopAllSitVariants();
-		gAgent.sendAnimationRequest(override(ANIM_AGENT_SIT, TRUE), ANIM_REQUEST_START);
-	}
-	else
-	{
-		ALAOSet::AOState* state = mCurrentSet->getState(ALAOSet::Sitting);
-		if (!state)
-		{
-			return;
-		}
-
-		LLUUID animation = state->mCurrentAnimationID;
-		if (animation.notNull())
-		{
-			gAgent.sendAnimationRequest(animation, ANIM_REQUEST_STOP);
-			gAgentAvatarp->LLCharacter::stopMotion(animation);
-			state->mCurrentAnimationID.setNull();
-		}
-
-		gAgent.sendAnimationRequest(ANIM_AGENT_SIT, ANIM_REQUEST_START);
-	}
-}
-
-void ALAOEngine::setSmart(ALAOSet* set, const bool smart)
-{
-	set->setSmart(smart);
-	set->setDirty(TRUE);
-
-	if (smart)
-	{
-		// make sure to restart the sit cancel timer to fix sit overrides when the object we are
-		// sitting on is playing its own animation
-		const LLViewerObject* agentRoot = dynamic_cast<LLViewerObject*>(gAgentAvatarp->getRoot());
-		if (agentRoot && agentRoot->getID() != gAgentID)
-		{
-			mSitCancelTimer.oneShot();
-		}
-	}
-}
-
-void ALAOEngine::setDisableStands(ALAOSet* set, const bool disable)
-{
-	set->setMouselookDisable(disable);
-	set->setDirty(true);
-
-	if (mCurrentSet != set)
-	{
-		return;
-	}
-
-	// make sure an update happens if needed
-	mInMouselook = !gAgentCamera.cameraMouselook();
-	inMouselook(!mInMouselook);
-}
-
-void ALAOEngine::setCycle(ALAOSet::AOState* state, const bool cycle)
-{
-	state->mCycle = cycle;
-	state->mDirty = true;
-}
-
-void ALAOEngine::setRandomize(ALAOSet::AOState* state, const bool randomize)
-{
-	state->mRandom = randomize;
-	state->mDirty = true;
-}
-
-void ALAOEngine::setCycleTime(ALAOSet::AOState* state, F32 time)
-{
-	state->mCycleTime = time;
-	state->mDirty = true;
-}
-
-void ALAOEngine::tick()
-{
-	if (!isAgentAvatarValid()) return;
-
-	if (mAOFolder.isNull())
-	{
-		gInventory.findCategoryUUIDForNameInRoot(ROOT_AO_FOLDER, gInventory.getRootFolderID(), true,
-			[&](const LLUUID& category_id)
-			{	mAOFolder = category_id;
-				LL_INFOS("AOEngine") << "AO basic folder structure intact." << LL_ENDL;
-				update(); 
-			});
-	}
-	else
-	{
-		update();
-	}
-}
-
-bool ALAOEngine::importNotecard(const LLInventoryItem* item)
-{
-	if (item)
-	{
-		LL_INFOS("AOEngine") << "importing AO notecard: " << item->getName() << LL_ENDL;
-		if (getSetByName(item->getName()))
-		{
-			LLNotificationsUtil::add("AOImportSetAlreadyExists", LLSD());
-			return false;
-		}
-
-		if (!gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE) && !gAgent.isGodlike())
-		{
-			LLNotificationsUtil::add("AOImportPermissionDenied", LLSD());
-			return false;
-		}
-
-		if (item->getAssetUUID().notNull())
-		{
-			mImportSet = new ALAOSet(item->getParentUUID());
-			if (!mImportSet)
-			{
-				LLNotificationsUtil::add("AOImportCreateSetFailed", LLSD());
-				return false;
-			}
-			mImportSet->setName(item->getName());
-
-			LLUUID* newUUID = new LLUUID(item->getAssetUUID());
-			const LLHost sourceSim = LLHost();
-
-			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 ALAOEngine::onNotecardLoadComplete(const LLUUID& assetUUID, LLAssetType::EType type,
-											void* userdata, S32 status, LLExtStat extStatus)
-{
-	if (status != LL_ERR_NOERR)
-	{
-		// AOImportDownloadFailed
-		LLNotificationsUtil::add("AOImportDownloadFailed", LLSD());
-		// NULL tells the importer to cancel all operations and free the import set memory
-		ALAOEngine::instance().parseNotecard(nullptr);
-		return;
-	}
-	LL_DEBUGS("AOEngine") << "Downloading import notecard complete." << LL_ENDL;
-
-	LLFileSystem file(assetUUID, type, LLFileSystem::READ);
-
-	S32 notecardSize = file.getSize();
-	auto buffer = std::make_unique<char[]>(notecardSize + 1);
-	buffer[notecardSize] = '\0';
-
-	if (file.read((U8*)buffer.get(), notecardSize) != FALSE)
-	{
-		ALAOEngine::instance().parseNotecard(std::move(buffer));
-	}
-	else
-	{
-		ALAOEngine::instance().parseNotecard(nullptr);
-	}
-}
-
-void ALAOEngine::parseNotecard(std::unique_ptr<char[]> buffer)
-{
-	LL_DEBUGS("AOEngine") << "parsing import notecard" << LL_ENDL;
-
-	bool isValid = false;
-
-	if (!buffer)
-	{
-		LL_WARNS("AOEngine") << "buffer==NULL - aborting import" << LL_ENDL;
-		// NOTE: cleanup is always the same, needs streamlining
-		delete mImportSet;
-		mImportSet = nullptr;
-		mUpdatedSignal();
-		return;
-	}
-
-	std::string text(buffer.get());
-
-	std::vector<std::string> lines;
-	LLStringUtil::getTokens(text, lines, "\n");
-
-	S32 found = -1;
-	for (U32 index = 0; index < lines.size(); ++index)
-	{
-		if (lines[index].find("Text length ") == 0)
-		{
-			found = index;
-			break;
-		}
-	}
-
-	if (found == -1)
-	{
-		LLNotificationsUtil::add("AOImportNoText", LLSD());
-		delete mImportSet;
-		mImportSet = nullptr;
-		mUpdatedSignal();
-		return;
-	}
-
-	LLViewerInventoryCategory* importCategory = gInventory.getCategory(mImportSet->getInventoryUUID());
-	if (!importCategory)
-	{
-		LLNotificationsUtil::add("AOImportNoFolder", LLSD());
-		delete mImportSet;
-		mImportSet = 0;
-		mUpdatedSignal();
-		return;
-	}
-
-	std::map<std::string, LLUUID> animationMap;
-	LLInventoryModel::cat_array_t* dummy;
-	LLInventoryModel::item_array_t* items;
-
-	gInventory.getDirectDescendentsOf(mImportSet->getInventoryUUID(), dummy, items);
-	for (const auto& inv_item : *items)
-    {
-        animationMap[inv_item->getName()] = inv_item->getUUID();
-		LL_DEBUGS("AOEngine")	<<	"animation " << inv_item->getName() <<
-						" has inventory UUID " << animationMap[inv_item->getName()] << LL_ENDL;
-	}
-
-	// [ State ]Anim1|Anim2|Anim3
-	for (U32 index = found + 1; index < lines.size(); ++index)
-	{
-		std::string line = lines[index];
-
-		// cut off the trailing } of a notecard asset's text portion in the last line
-		if (index == lines.size() - 1)
-		{
-			line = line.substr(0, line.size() - 1);
-		}
-
-		LLStringUtil::trim(line);
-
-		if (line.empty() || line[0] == '#') continue;
-
-		if (line.find('[') != 0)
-		{
-			LLSD args;
-			args["LINE"] = (S32)index;
-			LLNotificationsUtil::add("AOImportNoStatePrefix", args);
-			continue;
-		}
-
-		size_t endTag = line.find(']');
-		if (endTag == std::string::npos)
-		{
-			LLSD args;
-			args["LINE"] = (S32)index;
-			LLNotificationsUtil::add("AOImportNoValidDelimiter", args);
-			continue;
-		}
-
-
-		std::string stateName = line.substr(1, endTag - 1);
-		LLStringUtil::trim(stateName);
-
-		// Recognized but not currently implemented. Handled here to avoid misleading "state not found" notification.
-		if ("Standing mode 2" == stateName || "Standing Calm" == stateName)
-		{
-			continue;
-		}
-
-		ALAOSet::AOState* newState = mImportSet->getStateByName(stateName);
-		if (!newState)
-		{
-			LLSD args;
-			args["NAME"] = stateName;
-			LLNotificationsUtil::add("AOImportStateNameNotFound", args);
-			continue;
-		}
-
-		std::string animationLine = line.substr(endTag + 1);
-		std::vector<std::string> animationList;
-		LLStringUtil::getTokens(animationLine, animationList, "|,");
-
-		for (U32 animIndex = 0; animIndex < animationList.size(); ++animIndex)
-		{
-			ALAOSet::AOAnimation animation;
-			animation.mName = animationList[animIndex];
-			animation.mInventoryUUID = animationMap[animation.mName];
-			if (animation.mInventoryUUID.isNull())
-			{
-				LLSD args;
-				args["NAME"] = animation.mName;
-				LLNotificationsUtil::add("AOImportAnimationNotFound", args);
-				continue;
-			}
-			animation.mSortOrder = animIndex;
-			newState->mAnimations.push_back(animation);
-			isValid = true;
-		}
-
-		if ("Standing mode 1" == stateName)
-		{
-			newState->mCycle = true;
-			newState->mCycleTime = 30.0f;
-			newState->mDirty = true;
-		}
-	}
-
-	if (!isValid)
-	{
-		LLNotificationsUtil::add("AOImportInvalid", LLSD());
-		// NOTE: cleanup is always the same, needs streamlining
-		delete mImportSet;
-		mImportSet = nullptr;
-		mUpdatedSignal();
-		return;
-	}
-
-	mTimerCollection.setImportTimer(true);
-	mImportRetryCount = 0;
-	processImport(false);
-}
-
-void ALAOEngine::processImportInternal(bool aFromTimer)
-{
-	bool allComplete = true;
-	for (S32 index = 0; index < ALAOSet::AOSTATES_MAX; ++index)
-	{
-		ALAOSet::AOState* state = mImportSet->getState(index);
-		if (!state->mAnimations.empty())
-		{
-			if (state->mCycleTime)
-			{
-				const std::string oldName = state->mName + ":CT";
-				state->mName = llformat("%s%d", oldName.c_str(), state->mCycleTime);
-			}
-			if (state->mCycle)
-			{
-				const std::string& oldName = state->mName;
-				state->mName = llformat("%s%s", oldName.c_str(), ":CY");
-			}
-			allComplete = false;
-			LL_DEBUGS("AOEngine") << "state " << state->mName << " still has animations to link." << LL_ENDL;
-
-			for (S32 animationIndex = state->mAnimations.size() - 1; animationIndex >= 0; --animationIndex)
-			{
-				LL_DEBUGS("AOEngine") << "linking animation " << state->mAnimations[animationIndex].mName << LL_ENDL;
-				if (createAnimationLink(mImportSet, state, gInventory.getItem(state->mAnimations[animationIndex].mInventoryUUID)))
-				{
-					LL_DEBUGS("AOEngine") << "link success, size " << state->mAnimations.size() << ", removing animation "
-						<< (*(state->mAnimations.begin() + animationIndex)).mName << " from import state" << LL_ENDL;
-					state->mAnimations.erase(state->mAnimations.begin() + animationIndex);
-					LL_DEBUGS("AOEngine") << "deleted, size now: " << state->mAnimations.size() << LL_ENDL;
-				}
-				else
-				{
-					LLSD args;
-					args["NAME"] = state->mAnimations[animationIndex].mName;
-					LLNotificationsUtil::add("AOImportLinkFailed", args);
-				}
-			}
-		}
-	}
-
-	if (allComplete)
-	{
-		mTimerCollection.setImportTimer(false);
-		mOldImportSets.push_back(mImportSet);
-		mImportSet = nullptr;
-		mImportCategory.setNull();
-		reload(aFromTimer);
-	}
-}
-
-void ALAOEngine::processImportNewCat(const LLUUID& id, bool process)
-{
-	mImportCategory = id;
-		if (mImportCategory.isNull())
-		{
-			mImportRetryCount++;
-			if (mImportRetryCount == 5)
-			{
-				// NOTE: cleanup is the same as at the end of this function. Needs streamlining.
-				mTimerCollection.setImportTimer(false);
-				delete mImportSet;
-				mImportSet = NULL;
-				mImportCategory.setNull();
-				mUpdatedSignal();
-				LLSD args;
-				args["NAME"] = mImportSet->getName();
-				LLNotificationsUtil::add("AOImportAbortCreateSet", args);
-			}
-			else
-			{
-				LLSD args;
-				args["NAME"] = mImportSet->getName();
-				LLNotificationsUtil::add("AOImportRetryCreateSet", args);
-			}
-			return;
-		}
-	mImportSet->setInventoryUUID(mImportCategory);
-
-	processImportInternal(process);
-}
-
-void ALAOEngine::processImport(bool aFromTimer)
-{
-	if (mImportCategory.isNull())
-	{
-		addSet(mImportSet->getName(), false, [this, aFromTimer](const LLUUID& newid) { processImportNewCat(newid, aFromTimer); });
-	}
-	else
-	{
-		processImportInternal(aFromTimer);
-	}
-}
-
-const LLUUID& ALAOEngine::getAOFolder() const
-{
-	return mAOFolder;
-}
-
-void ALAOEngine::onRegionChange()
-{
-	// do nothing if the AO is off
-	if (!mEnabled) return;
-
-	// catch errors without crashing
-	if (!mCurrentSet)
-	{
-		LL_DEBUGS("AOEngine") << "Current set was NULL" << LL_ENDL;
-		return;
-	}
-
-	// sitting needs special attention
-	if (mCurrentSet->getMotion() == ANIM_AGENT_SIT)
-	{
-		// do nothing if sit overrides was disabled
-		if (!mCurrentSet->getSitOverride())
-		{
-			return;
-		}
-
-		// do nothing if the last overridden motion wasn't a sit.
-		// happens when sit override is enabled but there were no
-		// sit animations added to the set yet
-		if (mLastOverriddenMotion != ANIM_AGENT_SIT)
-		{
-			return;
-		}
-
-		// do nothing if smart sit is enabled because we have no
-		// animation running from the AO
-		if (mCurrentSet->getSmart())
-		{
-			return;
-		}
-	}
-
-	// restart current animation on region crossing
-	gAgent.sendAnimationRequest(mLastMotion, ANIM_REQUEST_START);
-}
-
-// ----------------------------------------------------
-
-ALAOSitCancelTimer::ALAOSitCancelTimer()
-:	LLEventTimer(0.1f),
-	mTickCount(0)
-{
-	mEventTimer.stop();
-}
-
-void ALAOSitCancelTimer::oneShot()
-{
-	mTickCount = 0;
-	mEventTimer.start();
-}
-
-void ALAOSitCancelTimer::stop()
-{
-	mEventTimer.stop();
-}
-
-BOOL ALAOSitCancelTimer::tick()
-{
-	mTickCount++;
-	ALAOEngine::instance().checkSitCancel();
-	if (mTickCount == 10)
-	{
-		mEventTimer.stop();
-	}
-	return FALSE;
-}
-
-// ----------------------------------------------------
-
-ALAOTimerCollection::ALAOTimerCollection()
-:	LLEventTimer(INVENTORY_POLLING_INTERVAL),
-	mInventoryTimer(true),
-	mSettingsTimer(false),
-	mReloadTimer(false),
-	mImportTimer(false)
-{
-	updateTimers();
-}
-
-BOOL ALAOTimerCollection::tick()
-{
-	if (mInventoryTimer)
-	{
-		LL_DEBUGS("AOEngine") << "Inventory timer tick()" << LL_ENDL;
-		ALAOEngine::instance().tick();
-	}
-	if (mSettingsTimer)
-	{
-		LL_DEBUGS("AOEngine") << "Settings timer tick()" << LL_ENDL;
-		ALAOEngine::instance().saveSettings();
-	}
-	if (mReloadTimer)
-	{
-		LL_DEBUGS("AOEngine") << "Reload timer tick()" << LL_ENDL;
-		ALAOEngine::instance().reload(true);
-	}
-	if (mImportTimer)
-	{
-		LL_DEBUGS("AOEngine") << "Import timer tick()" << LL_ENDL;
-		ALAOEngine::instance().processImport(true);
-	}
-
-	// always return FALSE or the LLEventTimer will be deleted -> crash
-	return FALSE;
-}
-
-void ALAOTimerCollection::setInventoryTimer(const bool enable)
-{
-	mInventoryTimer = enable;
-	updateTimers();
-}
-
-void ALAOTimerCollection::setSettingsTimer(const bool enable)
-{
-	mSettingsTimer = enable;
-	updateTimers();
-}
-
-void ALAOTimerCollection::setReloadTimer(const bool enable)
-{
-	mReloadTimer = enable;
-	updateTimers();
-}
-
-void ALAOTimerCollection::setImportTimer(const bool enable)
-{
-	mImportTimer = enable;
-	updateTimers();
-}
-
-void ALAOTimerCollection::updateTimers()
-{
-	if (!mInventoryTimer && !mSettingsTimer && !mReloadTimer && !mImportTimer)
-	{
-		LL_DEBUGS("AOEngine") << "no timer needed, stopping internal timer." << LL_ENDL;
-		mEventTimer.stop();
-	}
-	else
-	{
-		LL_DEBUGS("AOEngine") << "timer needed, starting internal timer." << LL_ENDL;
-		mEventTimer.start();
-	}
-}
diff --git a/indra/newview/alaoengine.h b/indra/newview/alaoengine.h
deleted file mode 100644
index 6568f71b559..00000000000
--- a/indra/newview/alaoengine.h
+++ /dev/null
@@ -1,227 +0,0 @@
-/**
- * @file alaoengine.h
- * @brief The core Animation Overrider engine
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2011, Zi Ree @ Second Life
- * Copyright (C) 2020, Rye Mutt <rye@alchemyviewer.org>
- *
- * 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
- * $/LicenseInfo$
- */
-
-#ifndef AL_AOENGINE_H
-#define AL_AOENGINE_H
-
-#include <boost/signals2.hpp>
-
-#include "alaoset.h"
-
-#include "llassettype.h"
-#include "lleventtimer.h"
-
-#include "llextendedstatus.h"
-#include "llsingleton.h"
-#include "llviewerinventory.h"
-
-class ALAOTimerCollection final : public LLEventTimer
-{
-public:
-	ALAOTimerCollection();
-	~ALAOTimerCollection() = default;
-
-	BOOL tick() override;
-
-	void setInventoryTimer(const bool enable);
-	void setSettingsTimer(const bool enable);
-	void setReloadTimer(const bool enable);
-	void setImportTimer(const bool enable);
-
-protected:
-	void updateTimers();
-
-	bool mInventoryTimer;
-	bool mSettingsTimer;
-	bool mReloadTimer;
-	bool mImportTimer;
-};
-
-// ----------------------------------------------------
-
-class ALAOSitCancelTimer final : public LLEventTimer
-{
-public:
-	ALAOSitCancelTimer();
-	~ALAOSitCancelTimer() = default;
-
-	void oneShot();
-	void stop();
-
-	BOOL tick() override;
-
-protected:
-	S32 mTickCount;
-};
-
-// ----------------------------------------------------
-
-class LLInventoryItem;
-class LLVFS;
-
-class ALAOEngine final : public LLSingleton<ALAOEngine>
-{
-	LLSINGLETON(ALAOEngine);
-	~ALAOEngine();
-
-public:
-	typedef enum e_cycle_mode
-	{
-		CycleAny,
-		CycleNext,
-		CyclePrevious
-	} eCycleMode;
-
-	void enable(const bool enable);
-	const LLUUID override(const LLUUID& motion, const bool start);
-	void tick();
-	void update();
-	void reload(const bool reload);
-	void reloadStateAnimations(ALAOSet::AOState* state);
-	void clear(const bool clear);
-
-	const LLUUID& getAOFolder() const;
-
-	void addSet(const std::string& name, bool reload = true, inventory_func_type callback = {});
-	bool removeSet(ALAOSet* set);
-
-	bool addAnimation(const ALAOSet* set, ALAOSet::AOState* state,
-					  const LLInventoryItem* item, bool reload = true);
-	bool removeAnimation(const ALAOSet* set, ALAOSet::AOState* state, S32 index);
-	void checkSitCancel();
-	void checkBelowWater(const bool under);
-
-	bool importNotecard(const LLInventoryItem* item);
-	void processImport(const bool process);
-	void processImportInternal(const bool process);
-	void processImportNewCat(const LLUUID&, bool process);
-
-	bool swapWithPrevious(ALAOSet::AOState* state, S32 index);
-	bool swapWithNext(ALAOSet::AOState* state, S32 index);
-
-	void cycleTimeout(const ALAOSet* set);
-	void cycle(eCycleMode cycleMode);
-
-	void inMouselook(const bool in_mouselook);
-	void selectSet(ALAOSet* set);
-	ALAOSet* selectSetByName(const std::string& name);
-	ALAOSet* getSetByName(const std::string& name) const;
-
-	// callback from LLAppViewer
-	static void onLoginComplete();
-
-	const std::vector<ALAOSet*>& getSetList() const;
-	const std::string& getCurrentSetName() const;
-	const ALAOSet* getDefaultSet() const;
-	bool renameSet(ALAOSet* set, const std::string& name);
-
-	void setDefaultSet(ALAOSet* set);
-	void setOverrideSits(ALAOSet* set, const bool override_sits);
-	void setSmart(ALAOSet* set, const bool smart);
-	void setDisableStands(ALAOSet* set, const bool disable);
-	void setCycle(ALAOSet::AOState* set, const bool cycle);
-	void setRandomize(ALAOSet::AOState* state, const bool randomize);
-	void setCycleTime(ALAOSet::AOState* state, const F32 time);
-
-	void saveSettings();
-
-	typedef boost::signals2::signal<void ()> updated_signal_t;
-	boost::signals2::connection setReloadCallback(const updated_signal_t::slot_type& cb)
-	{
-		return mUpdatedSignal.connect(cb);
-	};
-
-	typedef boost::signals2::signal<void (const LLUUID&)> animation_changed_signal_t;
-	boost::signals2::connection setAnimationChangedCallback(const animation_changed_signal_t::slot_type& cb)
-	{
-		return mAnimationChangedSignal.connect(cb);
-	};
-	
-	typedef boost::signals2::signal<void (const std::string&)> set_changed_signal_t;
-	boost::signals2::connection setSetChangedCallback(const set_changed_signal_t::slot_type& cb)
-	{
-		return mSetChangedSignal.connect(cb);
-	}
-
-protected:
-	void init();
-
-	void setLastMotion(const LLUUID& motion);
-	void setLastOverriddenMotion(const LLUUID& motion);
-	void setStateCycleTimer(const ALAOSet::AOState* state);
-
-	void stopAllStandVariants();
-	void stopAllSitVariants();
-
-	bool foreignAnimations(const LLUUID& seat);
-	const LLUUID& mapSwimming(const LLUUID& motion) const;
-
-	void updateSortOrder(ALAOSet::AOState* state);
-	void saveSet(const ALAOSet* set);
-	void saveState(const ALAOSet::AOState* state);
-
-	bool createAnimationLink(const ALAOSet* set, ALAOSet::AOState* state, const LLInventoryItem* item);
-	bool findForeignItems(const LLUUID& uuid) const;
-	void purgeFolder(const LLUUID& uuid) const;
-
-	void onRegionChange();
-
-	void onToggleAOControl();
-	static void onNotecardLoadComplete(const LLUUID& assetUUID, LLAssetType::EType type,
-												void* userdata, S32 status, LLExtStat extStatus);
-
-	void parseNotecard(std::unique_ptr<char[]> buffer);
-
-	updated_signal_t mUpdatedSignal;
-	animation_changed_signal_t mAnimationChangedSignal;
-	set_changed_signal_t mSetChangedSignal;
-
-	ALAOTimerCollection mTimerCollection;
-	ALAOSitCancelTimer mSitCancelTimer;
-
-	bool mEnabled;
-	bool mInMouselook;
-	bool mUnderWater;
-
-	LLUUID mAOFolder;
-	LLUUID mLastMotion;
-	LLUUID mLastOverriddenMotion;
-
-	std::vector<ALAOSet*> mSets;
-	std::vector<ALAOSet*> mOldSets;
-	ALAOSet* mCurrentSet;
-	ALAOSet* mDefaultSet;
-	
-	ALAOSet* mImportSet;
-	std::vector<ALAOSet*> mOldImportSets;
-	LLUUID mImportCategory;
-	S32 mImportRetryCount;
-
-	boost::signals2::connection mRegionChangeConnection;
-};
-
-extern const std::string ROOT_AO_FOLDER;
-
-#endif // LL_AOENGINE_H
diff --git a/indra/newview/alaoset.h b/indra/newview/alaoset.h
deleted file mode 100644
index 5d40068bc9b..00000000000
--- a/indra/newview/alaoset.h
+++ /dev/null
@@ -1,152 +0,0 @@
-/**
- * @file alaoset.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
- * Copyright (C) 2016, Cinder Roxley <cinder@sdf.org>
- * Copyright (C) 2020, Rye Mutt <rye@alchemyviewer.org>
- *
- * 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
- * $/LicenseInfo$
- */
-
-#ifndef AL_AOSET_H
-#define AL_AOSET_H
-
-#include <array>
-#include <utility>
-#include "lleventtimer.h"
-
-class ALAOSet final : public LLEventTimer
-{
-public:
-	ALAOSet(const LLUUID& inventoryID);
-	~ALAOSet();
-
-	// 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,
-		SoftLanding,
-		StandingUp,
-		Falling,
-		FlyingDown,
-		FlyingUp,
-		Flying,
-		FlyingSlow,
-		Hovering,
-		Jumping,
-		PreJumping,
-		TurningRight,
-		TurningLeft,
-		Typing,
-		Floating,
-		SwimmingForward,
-		SwimmingUp,
-		SwimmingDown,
-		AOSTATES_MAX
-	};
-
-	struct AOAnimation
-	{
-		AOAnimation(): mSortOrder( 0 ) {}
-		AOAnimation(std::string name, const LLUUID& asset_id, const LLUUID& inv_id, S32 sort_order)
-			: mName(std::move(name))
-			, mAssetUUID(asset_id)
-			, mInventoryUUID(inv_id)
-			, mSortOrder(sort_order)
-		{}
-		std::string mName;
-		LLUUID mAssetUUID;
-		LLUUID mInventoryUUID;
-		S32 mSortOrder;
-	};
-
-	struct AOState
-	{
-		std::string mName;
-		std::vector<std::string> mAlternateNames;
-		LLUUID mRemapID;
-		bool mCycle;
-		bool mRandom;
-		S32 mCycleTime;
-		std::vector<AOAnimation> mAnimations;
-		U32 mCurrentAnimation;
-		LLUUID mCurrentAnimationID;
-		LLUUID mInventoryUUID;
-		bool mDirty;
-	};
-
-	// getters and setters
-	const LLUUID& getInventoryUUID() const { return mInventoryID; }
-	void setInventoryUUID(const LLUUID& inv_id) { mInventoryID = inv_id; }
-
-	const std::string& getName() const { return mName; }
-	void setName(const std::string& name) { mName = name; }
-
-	bool getSitOverride() const { return mSitOverride; }
-	void setSitOverride(const bool sit_override) { mSitOverride = sit_override; }
-
-	bool getSmart() const { return mSmart; }
-	void setSmart(const bool smart) { mSmart = smart; }
-
-	bool getMouselookDisable() const { return mMouselookDisable; }
-	void setMouselookDisable(const bool disable) { mMouselookDisable = disable; }
-
-	bool getComplete() const { return mComplete; }
-	void setComplete(const bool complete) { mComplete = complete; }
-
-	const LLUUID& getMotion() const { return mCurrentMotion; }
-	void setMotion(const LLUUID& motion) { mCurrentMotion = motion; }
-
-	bool getDirty() const { return mDirty; }
-	void setDirty(const bool dirty) { mDirty = dirty; }
-
-	AOState* getState(S32 name);
-	AOState* getStateByName(const std::string& name);
-	AOState* getStateByRemapID(const LLUUID& id);
-	const LLUUID& getAnimationForState(AOState* state) const;
-
-	void startTimer(F32 timeout);
-	void stopTimer();
-	virtual BOOL tick() override;
-
-	std::vector<std::string> mStateNames;
-
-private:
-	LLUUID mInventoryID;
-
-	std::string mName;
-	bool mSitOverride;
-	bool mSmart;
-	bool mMouselookDisable;
-	bool mComplete;
-	LLUUID mCurrentMotion;
-	bool mDirty;
-
-	std::array<AOState, AOSTATES_MAX> mStates;
-};
-
-#endif // LL_AOSET_H
diff --git a/indra/newview/alchatcommand.cpp b/indra/newview/alchatcommand.cpp
index eee0a53329c..40786a66f0c 100644
--- a/indra/newview/alchatcommand.cpp
+++ b/indra/newview/alchatcommand.cpp
@@ -29,7 +29,7 @@
 #include "object_flags.h"
 
 // viewer includes
-//#include "alaoengine.h"
+#include "aoengine.h"
 #include "llagent.h"
 #include "llagentcamera.h"
 #include "llagentui.h"
@@ -287,7 +287,6 @@ bool ALChatCommand::parseCommand(std::string data)
 			}
 			return true;
 		}
-#if 0
         else if (cmd == utf8str_tolower(sAOCommand()))
 		{
 			std::string subcmd;
@@ -305,28 +304,27 @@ bool ALChatCommand::parseCommand(std::string data)
 				}
 				else if (subcmd == "sit")
 				{
-					auto ao_set = ALAOEngine::instance().getSetByName(ALAOEngine::instance().getCurrentSetName());
+					auto ao_set = AOEngine::instance().getSetByName(AOEngine::instance().getCurrentSetName());
 					if (input >> subcmd)
 					{
 						if (subcmd == "on")
 						{
-							ALAOEngine::instance().setOverrideSits(ao_set, true);
+							AOEngine::instance().setOverrideSits(ao_set, true);
 
 						}
 						else if (subcmd == "off")
 						{
-							ALAOEngine::instance().setOverrideSits(ao_set, false);
+							AOEngine::instance().setOverrideSits(ao_set, false);
 						}
 					}
 					else
 					{
-						ALAOEngine::instance().setOverrideSits(ao_set, !ao_set->getSitOverride());
+						AOEngine::instance().setOverrideSits(ao_set, !ao_set->getSitOverride());
 					}
 					return true;
 				}
 			}
 		}
-#endif
         else if (cmd == "/sendmenu")
         {
             S32 channel;
diff --git a/indra/newview/alfloaterao.cpp b/indra/newview/alfloaterao.cpp
deleted file mode 100644
index e62848d4c89..00000000000
--- a/indra/newview/alfloaterao.cpp
+++ /dev/null
@@ -1,740 +0,0 @@
-/**
- * @file alfloaterao.cpp
- * @brief Animation overrider controls
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * Copyright (C) 2011, Zi Ree @ Second Life
- * Copyright (C) 2016, Cinder <cinder@sdf.org>
- * Copyright (C) 2020, Rye Mutt <rye@alchemyviewer.org>
- *
- * 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 "alfloaterao.h"
-#include "alaoengine.h"
-#include "alaoset.h"
-#include "llcheckboxctrl.h"
-#include "llcombobox.h"
-#include "llfloaterreg.h"
-#include "llnotificationsutil.h"
-#include "llspinctrl.h"
-#include "llviewercontrol.h"
-#include "llviewerinventory.h"
-
-ALFloaterAO::ALFloaterAO(const LLSD& key) : LLTransientDockableFloater(nullptr, true, key)
-, LLEventTimer(10.f)
-, mSetList()
-, mSelectedSet(nullptr)
-, mSelectedState(nullptr)
-, mReloadCoverPanel(nullptr)
-, mMainInterfacePanel(nullptr)
-, mSetSelector(nullptr)
-, mActivateSetButton(nullptr)
-, mAddButton(nullptr)
-, mRemoveButton(nullptr)
-, mDefaultCheckBox(nullptr)
-, mOverrideSitsCheckBox(nullptr)
-, mSmartCheckBox(nullptr)
-, mDisableMouselookCheckBox(nullptr)
-, mStateSelector(nullptr)
-, mAnimationList(nullptr)
-, mCurrentBoldItem(nullptr)
-, mMoveUpButton(nullptr)
-, mMoveDownButton(nullptr)
-, mTrashButton(nullptr)
-, mCycleCheckBox(nullptr)
-, mRandomizeCheckBox(nullptr)
-, mCycleTimeTextLabel(nullptr)
-, mCycleTimeSpinner(nullptr)
-, mReloadButton(nullptr)
-, mPreviousButton(nullptr)
-, mNextButton(nullptr)
-, mCanDragAndDrop(false)
-, mImportRunning(false)
-{
-	mEventTimer.stop();
-	
-	mCommitCallbackRegistrar.add("AO.Reload", boost::bind(&ALFloaterAO::onClickReload, this));
-	mCommitCallbackRegistrar.add("AO.ActivateSet", boost::bind(&ALFloaterAO::onClickActivate, this));
-	mCommitCallbackRegistrar.add("AO.AddSet", boost::bind(&ALFloaterAO::onClickAdd, this));
-	mCommitCallbackRegistrar.add("AO.RemoveSet", boost::bind(&ALFloaterAO::onClickRemove, this));
-	mCommitCallbackRegistrar.add("AO.SelectSet", boost::bind(&ALFloaterAO::onSelectSet, this, _2));
-	mCommitCallbackRegistrar.add("AO.SelectState", boost::bind(&ALFloaterAO::onSelectState, this));
-	mCommitCallbackRegistrar.add("AO.NextAnim", boost::bind(&ALFloaterAO::onClickNext, this));
-	mCommitCallbackRegistrar.add("AO.PrevAnim", boost::bind(&ALFloaterAO::onClickPrevious, this));
-	mCommitCallbackRegistrar.add("AO.SetCycle", boost::bind(&ALFloaterAO::onCheckCycle, this));
-	mCommitCallbackRegistrar.add("AO.SetCycleTime", boost::bind(&ALFloaterAO::onChangeCycleTime, this));
-	mCommitCallbackRegistrar.add("AO.SetDefault", boost::bind(&ALFloaterAO::onCheckDefault, this));
-	mCommitCallbackRegistrar.add("AO.SetRandomize", boost::bind(&ALFloaterAO::onCheckRandomize, this));
-	mCommitCallbackRegistrar.add("AO.SetSitOverride", boost::bind(&ALFloaterAO::onCheckOverrideSits, this));
-	mCommitCallbackRegistrar.add("AO.SetSmart", boost::bind(&ALFloaterAO::onCheckSmart, this));
-	mCommitCallbackRegistrar.add("AO.DisableStandsML", boost::bind(&ALFloaterAO::onCheckDisableStands, this));
-	mCommitCallbackRegistrar.add("AO.SelectAnim", boost::bind(&ALFloaterAO::onChangeAnimationSelection, this));
-	mCommitCallbackRegistrar.add("AO.RemoveAnim", boost::bind(&ALFloaterAO::onClickTrash, this));
-	mCommitCallbackRegistrar.add("AO.MoveAnimUp", boost::bind(&ALFloaterAO::onClickMoveUp, this));
-	mCommitCallbackRegistrar.add("AO.MoveAnimDown", boost::bind(&ALFloaterAO::onClickMoveDown, this));
-	
-	//mEnableCallbackRegistrar.add("AO.EnableSet", boost::bind());
-	//mEnableCallbackRegistrar.add("AO.EnableState", boost::bind());
-}
-
-ALFloaterAO::~ALFloaterAO()
-{
-	if (mReloadCallback.connected())
-		mReloadCallback.disconnect();
-	if (mAnimationChangedCallback.connected())
-		mAnimationChangedCallback.disconnect();
-}
-
-void ALFloaterAO::reloading(const bool reload)
-{
-	if (reload)
-		mEventTimer.start();
-	else
-		mEventTimer.stop();
-	
-	mReloadCoverPanel->setVisible(reload);
-	enableSetControls(!reload);
-	enableStateControls(!reload);
-}
-
-BOOL ALFloaterAO::tick()
-{
-	// reloading took too long, probably missed the signal, so we hide the reload cover
-	LL_WARNS("AOEngine") << "AO reloading timeout." << LL_ENDL;
-	updateList();
-	return FALSE;
-}
-
-void ALFloaterAO::updateSetParameters()
-{
-	mOverrideSitsCheckBox->setValue(mSelectedSet->getSitOverride());
-	mSmartCheckBox->setValue(mSelectedSet->getSmart());
-	mDisableMouselookCheckBox->setValue(mSelectedSet->getMouselookDisable());
-	BOOL isDefault = (mSelectedSet == ALAOEngine::instance().getDefaultSet()) ? TRUE : FALSE;
-	mDefaultCheckBox->setValue(isDefault);
-	mDefaultCheckBox->setEnabled(!isDefault);
-	updateSmart();
-}
-
-void ALFloaterAO::updateAnimationList()
-{
-	S32 currentStateSelected = mStateSelector->getCurrentIndex();
-	
-	mStateSelector->removeall();
-	onChangeAnimationSelection();
-	
-	if (!mSelectedSet)
-	{
-		mStateSelector->setEnabled(FALSE);
-		mStateSelector->add(getString("ao_no_animations_loaded"));
-		return;
-	}
-	
-	for (auto const& stateName : mSelectedSet->mStateNames)
-    {
-        ALAOSet::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 ALFloaterAO::updateList()
-{
-	mReloadButton->setEnabled(TRUE);
-	mImportRunning = false;
-	
-	std::string currentSetName = mSetSelector->getSelectedItemLabel();
-	if (currentSetName.empty())
-	{
-		currentSetName = ALAOEngine::instance().getCurrentSetName();
-	}
-	
-	mSetList = ALAOEngine::instance().getSetList();
-	mSetSelector->removeall();
-	mSetSelector->clear();
-	
-	mAnimationList->deleteAllItems();
-	mCurrentBoldItem = nullptr;
-	reloading(false);
-	
-	if (mSetList.empty())
-	{
-		LL_DEBUGS("AOEngine") << "empty set list" << LL_ENDL;
-		mSetSelector->add(getString("ao_no_sets_loaded"));
-		mSetSelector->selectNthItem(0);
-		enableSetControls(false);
-		return;
-	}
-	
-	for (U32 index = 0; index < mSetList.size(); ++index)
-	{
-		std::string setName = mSetList[index]->getName();
-		mSetSelector->add(setName, &mSetList[index], ADD_BOTTOM, TRUE);
-		if (setName.compare(currentSetName) == 0)
-		{
-			mSelectedSet = ALAOEngine::instance().selectSetByName(currentSetName);
-			mSetSelector->selectNthItem(index);
-			updateSetParameters();
-			updateAnimationList();
-		}
-	}
-	enableSetControls(true);
-}
-
-BOOL ALFloaterAO::postBuild()
-{
-	LLPanel* panel = getChild<LLPanel>("animation_overrider_outer_panel");
-	mMainInterfacePanel = panel->getChild<LLPanel>("animation_overrider_panel");
-	mReloadCoverPanel = panel->getChild<LLPanel>("ao_reload_cover");
-	
-	mSetSelector = mMainInterfacePanel->getChild<LLComboBox>("ao_set_selection_combo");
-	mActivateSetButton = mMainInterfacePanel->getChild<LLButton>("ao_activate");
-	mAddButton = mMainInterfacePanel->getChild<LLButton>("ao_add");
-	mRemoveButton = mMainInterfacePanel->getChild<LLButton>("ao_remove");
-	mDefaultCheckBox = mMainInterfacePanel->getChild<LLCheckBoxCtrl>("ao_default");
-	mOverrideSitsCheckBox = mMainInterfacePanel->getChild<LLCheckBoxCtrl>("ao_sit_override");
-	mSmartCheckBox = mMainInterfacePanel->getChild<LLCheckBoxCtrl>("ao_smart");
-	mDisableMouselookCheckBox = mMainInterfacePanel->getChild<LLCheckBoxCtrl>("ao_disable_stands_in_mouselook");
-	
-	mStateSelector = mMainInterfacePanel->getChild<LLComboBox>("ao_state_selection_combo");
-	mAnimationList = mMainInterfacePanel->getChild<LLScrollListCtrl>("ao_state_animation_list");
-	mMoveUpButton = mMainInterfacePanel->getChild<LLButton>("ao_move_up");
-	mMoveDownButton = mMainInterfacePanel->getChild<LLButton>("ao_move_down");
-	mTrashButton = mMainInterfacePanel->getChild<LLButton>("ao_trash");
-	mCycleCheckBox = mMainInterfacePanel->getChild<LLCheckBoxCtrl>("ao_cycle");
-	mRandomizeCheckBox = mMainInterfacePanel->getChild<LLCheckBoxCtrl>("ao_randomize");
-	mCycleTimeTextLabel = mMainInterfacePanel->getChild<LLTextBox>("ao_cycle_time_seconds_label");
-	mCycleTimeSpinner = mMainInterfacePanel->getChild<LLSpinCtrl>("ao_cycle_time");
-	
-	mReloadButton = mMainInterfacePanel->getChild<LLButton>("ao_reload");
-	mPreviousButton = mMainInterfacePanel->getChild<LLButton>("ao_previous");
-	mNextButton = mMainInterfacePanel->getChild<LLButton>("ao_next");
-	
-	mAnimationList->setCommitOnSelectionChange(TRUE);
-	
-	updateSmart();
-	
-	mReloadCallback = ALAOEngine::instance().setReloadCallback(boost::bind(&ALFloaterAO::updateList, this));
-	mAnimationChangedCallback = ALAOEngine::instance().setAnimationChangedCallback(boost::bind(&ALFloaterAO::onAnimationChanged, this, _1));
-	
-	onChangeAnimationSelection();
-	mMainInterfacePanel->setVisible(TRUE);
-	reloading(true);
-	
-	updateList();
-	
-	return LLDockableFloater::postBuild();
-}
-
-// static
-ALFloaterAO* ALFloaterAO::getInstance()
-{
-	return LLFloaterReg::getTypedInstance<ALFloaterAO>("ao");
-}
-
-void ALFloaterAO::enableSetControls(const bool enable)
-{
-	mSetSelector->setEnabled(enable);
-	mActivateSetButton->setEnabled(enable);
-	mRemoveButton->setEnabled(enable);
-	mDefaultCheckBox->setEnabled(enable);
-	mOverrideSitsCheckBox->setEnabled(enable);
-	mDisableMouselookCheckBox->setEnabled(enable);
-	
-	if (!enable)
-	{
-		enableStateControls(enable);
-	}
-}
-
-void ALFloaterAO::enableStateControls(const bool enable)
-{
-	mStateSelector->setEnabled(enable);
-	mAnimationList->setEnabled(enable);
-	mCycleCheckBox->setEnabled(enable);
-	if (enable)
-	{
-		updateCycleParameters();
-	}
-	else
-	{
-		mRandomizeCheckBox->setEnabled(enable);
-		mCycleTimeTextLabel->setEnabled(enable);
-		mCycleTimeSpinner->setEnabled(enable);
-	}
-	mPreviousButton->setEnabled(enable);
-	mNextButton->setEnabled(enable);
-	mCanDragAndDrop = enable;
-}
-
-void ALFloaterAO::onSelectSet(const LLSD& userdata)
-{
-	ALAOSet* set = ALAOEngine::instance().getSetByName(userdata.asString());
-	if (!set)
-	{
-		onRenameSet();
-		return;
-	}
-	
-	mSelectedSet = set;
-	
-	updateSetParameters();
-	updateAnimationList();
-}
-
-void ALFloaterAO::onRenameSet()
-{
-	if (!mSelectedSet)
-	{
-		LL_WARNS("AOEngine") << "Rename AO set without set selected." << LL_ENDL;
-		return;
-	}
-	
-	std::string name = mSetSelector->getSimple();
-	LLStringUtil::trim(name);
-	
-	LLUIString new_set_name = name;
-	
-	if (!name.empty())
-	{
-		if (
-			LLTextValidate::validateASCIIPrintableNoPipe(new_set_name.getWString()) &&	// only allow ASCII
-			name.find_first_of(":|") == std::string::npos)								// don't allow : or |
-		{
-			if (ALAOEngine::instance().renameSet(mSelectedSet, name))
-			{
-				reloading(true);
-				return;
-			}
-		}
-		else
-		{
-			LLNotificationsUtil::add("RenameAOMustBeASCII", LLSD().with("AO_SET_NAME", name));
-		}
-	}
-	mSetSelector->setSimple(mSelectedSet->getName());
-}
-
-void ALFloaterAO::onClickActivate()
-{
-	LL_DEBUGS("AOEngine") << "Set activated: " << mSetSelector->getSelectedItemLabel() << LL_ENDL;
-	ALAOEngine::instance().selectSet(mSelectedSet);
-}
-
-LLScrollListItem* ALFloaterAO::addAnimation(const std::string& name)
-{
-	LLSD row;
-	row["columns"][0]["column"] = "icon";
-	row["columns"][0]["type"] = "icon";
-	row["columns"][0]["value"] = "Inv_Animation";
-	
-	row["columns"][1]["column"] = "animation_name";
-	row["columns"][1]["type"] = "text";
-	row["columns"][1]["value"] = name;
-	
-	return mAnimationList->addElement(row);
-}
-
-void ALFloaterAO::onSelectState()
-{
-	mAnimationList->deleteAllItems();
-	mCurrentBoldItem = nullptr;
-	mAnimationList->setCommentText(getString("ao_no_animations_loaded"));
-	mAnimationList->setEnabled(FALSE);
-	
-	onChangeAnimationSelection();
-	
-	if (!mSelectedSet) return;
-	
-	mSelectedState = mSelectedSet->getStateByName(mStateSelector->getSelectedItemLabel());
-	if (!mSelectedState) return;
-	
-	mSelectedState = static_cast<ALAOSet::AOState*>(mStateSelector->getCurrentUserdata());
-	if (!mSelectedState->mAnimations.empty())
-	{
-		for (auto& ao_animation : mSelectedState->mAnimations)
-        {
-			LLScrollListItem* item = addAnimation(ao_animation.mName);
-			if (item)
-			{
-				item->setUserdata(&ao_animation.mInventoryUUID);
-			}
-		}
-		
-		mAnimationList->setCommentText(LLStringUtil::null);
-		mAnimationList->setEnabled(TRUE);
-	}
-	
-	mCycleCheckBox->setValue(mSelectedState->mCycle);
-	mRandomizeCheckBox->setValue(mSelectedState->mRandom);
-	mCycleTimeSpinner->setValue(mSelectedState->mCycleTime);
-	
-	updateCycleParameters();
-}
-
-void ALFloaterAO::onClickReload()
-{
-	reloading(true);
-	
-	mSelectedSet = nullptr;
-	mSelectedState = nullptr;
-	
-	ALAOEngine::instance().reload(false);
-	updateList();
-}
-
-void ALFloaterAO::onClickAdd()
-{
-	LLNotificationsUtil::add("NewAOSet", LLSD(), LLSD(),
-							 boost::bind(&ALFloaterAO::newSetCallback, this, _1, _2));
-}
-
-BOOL ALFloaterAO::newSetCallback(const LLSD& notification, const LLSD& response)
-{
-	std::string new_name = response["message"].asString();
-	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
-	
-	LLStringUtil::trim(new_name);
-	
-	LLUIString new_set_name = new_name;
-	
-	if (new_name.empty())
-	{
-		return FALSE;
-	}
-	else if (!LLTextValidate::validateASCIIPrintableNoPipe(new_set_name.getWString())		// only allow ASCII
-			 || new_name.find_first_of(":|") != std::string::npos)							// don't allow : or |
-	{
-		LLNotificationsUtil::add("NewAOCantContainNonASCII", LLSD().with("AO_SET_NAME", new_name));
-		return FALSE;
-	}
-	
-	if (option == 0)
-	{
-		ALAOEngine::instance().addSet(new_name);
-		{
-			reloading(true);
-			return TRUE;
-		}
-	}
-	return FALSE;
-}
-
-void ALFloaterAO::onClickRemove()
-{
-	if (!mSelectedSet) return;
-	
-	LLNotificationsUtil::add("RemoveAOSet", LLSD().with("AO_SET_NAME", mSelectedSet->getName()),
-							 LLSD(), boost::bind(&ALFloaterAO::removeSetCallback, this, _1, _2));
-}
-
-BOOL ALFloaterAO::removeSetCallback(const LLSD& notification, const LLSD& response)
-{
-	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
-	
-	if (option == 0)
-	{
-		if (ALAOEngine::instance().removeSet(mSelectedSet))
-		{
-			reloading(true);
-			// to prevent snapping back to deleted set
-			mSetSelector->removeall();
-			// visually indicate there are no items left
-			mSetSelector->clear();
-			mAnimationList->deleteAllItems();
-			mCurrentBoldItem = nullptr;
-			return TRUE;
-		}
-	}
-	return FALSE;
-}
-
-void ALFloaterAO::onCheckDefault()
-{
-	if (mSelectedSet)
-	{
-		ALAOEngine::instance().setDefaultSet(mSelectedSet);
-	}
-}
-
-void ALFloaterAO::onCheckOverrideSits()
-{
-	if (mSelectedSet)
-	{
-		ALAOEngine::instance().setOverrideSits(mSelectedSet, mOverrideSitsCheckBox->getValue().asBoolean());
-	}
-	updateSmart();
-}
-
-void ALFloaterAO::updateSmart()
-{
-	mSmartCheckBox->setEnabled(mOverrideSitsCheckBox->getValue());
-}
-
-void ALFloaterAO::onCheckSmart()
-{
-	if (mSelectedSet)
-	{
-		ALAOEngine::instance().setSmart(mSelectedSet, mSmartCheckBox->getValue().asBoolean());
-	}
-}
-
-void ALFloaterAO::onCheckDisableStands()
-{
-	if (mSelectedSet)
-	{
-		ALAOEngine::instance().setDisableStands(mSelectedSet, mDisableMouselookCheckBox->getValue().asBoolean());
-	}
-}
-
-void ALFloaterAO::onChangeAnimationSelection()
-{
-	std::vector<LLScrollListItem*> list = mAnimationList->getAllSelected();
-	LL_DEBUGS("AOEngine") << "Selection count: " << list.size() << LL_ENDL;
-	
-	BOOL resortEnable = FALSE;
-	BOOL trashEnable = FALSE;
-	
-	// Linden Lab bug: scroll lists still select the first item when you click on them, even when they are disabled.
-	// The control does not memorize it's enabled/disabled state, so mAnimationList->mEnabled() doesn't seem to work.
-	// So we need to safeguard against it.
-	if (!mCanDragAndDrop)
-	{
-		mAnimationList->deselectAllItems();
-		LL_DEBUGS("AOEngine") << "Selection count now: " << list.size() << LL_ENDL;
-	}
-	else if (!list.empty())
-	{
-		if (list.size() == 1)
-		{
-			resortEnable = TRUE;
-		}
-		trashEnable = TRUE;
-	}
-	
-	mMoveDownButton->setEnabled(resortEnable);
-	mMoveUpButton->setEnabled(resortEnable);
-	mTrashButton->setEnabled(trashEnable);
-}
-
-void ALFloaterAO::onClickMoveUp()
-{
-	if (!mSelectedState)
-	{
-		return;
-	}
-	
-	std::vector<LLScrollListItem*> list = mAnimationList->getAllSelected();
-	if (list.size() != 1)
-	{
-		return;
-	}
-	
-	S32 currentIndex = mAnimationList->getFirstSelectedIndex();
-	if (currentIndex == -1)
-	{
-		return;
-	}
-	
-	if (ALAOEngine::instance().swapWithPrevious(mSelectedState, currentIndex))
-	{
-		mAnimationList->swapWithPrevious(currentIndex);
-	}
-}
-
-void ALFloaterAO::onClickMoveDown()
-{
-	if (!mSelectedState)
-	{
-		return;
-	}
-	
-	std::vector<LLScrollListItem*> list = mAnimationList->getAllSelected();
-	if (list.size() != 1) return;
-	
-	S32 currentIndex = mAnimationList->getFirstSelectedIndex();
-	if (currentIndex >= (mAnimationList->getItemCount() - 1))
-	{
-		return;
-	}
-	
-	if (ALAOEngine::instance().swapWithNext(mSelectedState, currentIndex))
-	{
-		mAnimationList->swapWithNext(currentIndex);
-	}
-}
-
-void ALFloaterAO::onClickTrash()
-{
-	if (!mSelectedState) return;
-	
-	std::vector<LLScrollListItem*> list = mAnimationList->getAllSelected();
-	if (list.empty()) return;
-
-	for (auto iter = list.crbegin(), iter_end = list.crend(); iter != iter_end; ++iter)
-	{
-		ALAOEngine::instance().removeAnimation(mSelectedSet, mSelectedState, mAnimationList->getItemIndex(*iter));
-	}
-	
-	mAnimationList->deleteSelectedItems();
-	mCurrentBoldItem = nullptr;
-}
-
-void ALFloaterAO::updateCycleParameters()
-{
-	BOOL cycle = mCycleCheckBox->getValue().asBoolean();
-	mRandomizeCheckBox->setEnabled(cycle);
-	mCycleTimeTextLabel->setEnabled(cycle);
-	mCycleTimeSpinner->setEnabled(cycle);
-}
-
-void ALFloaterAO::onCheckCycle()
-{
-	if (mSelectedState)
-	{
-		bool cycle = mCycleCheckBox->getValue().asBoolean();
-		ALAOEngine::instance().setCycle(mSelectedState, cycle);
-		updateCycleParameters();
-	}
-}
-
-void ALFloaterAO::onCheckRandomize()
-{
-	if (mSelectedState)
-	{
-		ALAOEngine::instance().setRandomize(mSelectedState, mRandomizeCheckBox->getValue().asBoolean());
-	}
-}
-
-void ALFloaterAO::onChangeCycleTime()
-{
-	if (mSelectedState)
-	{
-		ALAOEngine::instance().setCycleTime(mSelectedState, mCycleTimeSpinner->getValueF32());
-	}
-}
-
-void ALFloaterAO::onClickPrevious()
-{
-	ALAOEngine::instance().cycle(ALAOEngine::CyclePrevious);
-}
-
-void ALFloaterAO::onClickNext()
-{
-	ALAOEngine::instance().cycle(ALAOEngine::CycleNext);
-}
-
-void ALFloaterAO::onAnimationChanged(const LLUUID& animation)
-{
-	LL_DEBUGS("AOEngine") << "Received animation change to " << animation << LL_ENDL;
-	
-	if (mCurrentBoldItem)
-	{
-		LLScrollListText* column = static_cast<LLScrollListText*>(mCurrentBoldItem->getColumn(1));
-		column->setFontStyle(LLFontGL::NORMAL);
-		
-		mCurrentBoldItem = nullptr;
-	}
-	
-	if (animation.isNull()) return;
-	
-	std::vector<LLScrollListItem*> item_list = mAnimationList->getAllData();
-	for (LLScrollListItem* item : item_list)
-	{
-		LLUUID* id = static_cast<LLUUID*>(item->getUserdata());
-		
-		if (id == &animation)
-		{
-			mCurrentBoldItem = item;
-			
-			LLScrollListText* column = static_cast<LLScrollListText*>(mCurrentBoldItem->getColumn(1));
-			column->setFontStyle(LLFontGL::BOLD);
-			
-			return;
-		}
-	}
-}
-
-// virtual
-BOOL ALFloaterAO::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType type, void* data,
-								  EAcceptance* accept, std::string& tooltipMsg)
-{	
-	LLInventoryItem* item = (LLInventoryItem*)data;
-	
-	if (type == DAD_NOTECARD)
-	{
-		if (mImportRunning)
-		{
-			*accept = ACCEPT_NO;
-			return TRUE;
-		}
-		*accept = ACCEPT_YES_SINGLE;
-		if (item && drop)
-		{
-			if (ALAOEngine::instance().importNotecard(item))
-			{
-				reloading(true);
-				mReloadButton->setEnabled(FALSE);
-				mImportRunning = true;
-			}
-		}
-	}
-	else if (type == DAD_ANIMATION)
-	{
-		if (!drop && (!mSelectedSet || !mSelectedState || !mCanDragAndDrop))
-		{
-			*accept = ACCEPT_NO;
-			return TRUE;
-		}
-		*accept = ACCEPT_YES_MULTI;
-		if (item && drop)
-		{
-			if (ALAOEngine::instance().addAnimation(mSelectedSet, mSelectedState, item))
-			{
-				addAnimation(item->getName());
-				
-				// TODO: this would be the right thing to do, but it blocks multi drop
-				// before final release this must be resolved
-				reloading(true);
-			}
-		}
-	}
-	else
-	{
-		*accept = ACCEPT_NO;
-	}
-	
-	return TRUE;
-}
diff --git a/indra/newview/alfloaterao.h b/indra/newview/alfloaterao.h
deleted file mode 100644
index a51446200da..00000000000
--- a/indra/newview/alfloaterao.h
+++ /dev/null
@@ -1,146 +0,0 @@
-/**
- * @file alfloaterao.h
- * @brief Animation overrider controls
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * Copyright (C) 2011, Zi Ree @ Second Life
- * Copyright (C) 2016, Cinder <cinder@sdf.org>
- * Copyright (C) 2020, Rye Mutt <rye@alchemyviewer.org>
- *
- * 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 AL_FLOATERAO_H
-#define AL_FLOATERAO_H
-
-#include "lleventtimer.h"
-#include "lltransientdockablefloater.h"
-#include "alaoset.h"
-
-class LLButton;
-class LLComboBox;
-class LLCheckBoxCtrl;
-class LLScrollListCtrl;
-class LLScrollListItem;
-class LLSpinCtrl;
-class LLTextBox;
-
-class ALFloaterAO final : public LLTransientDockableFloater, public LLEventTimer
-{
-	friend class LLFloaterReg;
-	friend class LLPanelAOMini;
-	
-private:
-	ALFloaterAO(const LLSD& key);
-	~ALFloaterAO();
-	
-public:
-	BOOL postBuild() override;
-	void updateList();
-	void updateSetParameters();
-	void updateAnimationList();
-	static ALFloaterAO* getInstance();
-	
-	BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data,
-						   EAcceptance* accept, std::string& tooltip_msg) override;
-	
-protected:
-	void onClickPrevious();
-	void onClickNext();
-	ALAOSet* getSelectedSet() const { return mSelectedSet; }
-	
-private:
-	LLScrollListItem* addAnimation(const std::string& name);
-	void onAnimationChanged(const LLUUID& animation);
-	void reloading(const bool reload);
-	
-	void onSelectSet(const LLSD& userdata);
-	void onRenameSet();
-	void onSelectState();
-	void onChangeAnimationSelection();
-	void onClickReload();
-	void onClickAdd();
-	void onClickRemove();
-	void onClickActivate();
-	void onCheckDefault();
-	void onCheckOverrideSits();
-	void onCheckSmart();
-	void onCheckDisableStands();
-	void onClickMoveUp();
-	void onClickMoveDown();
-	void onClickTrash();
-	void onCheckCycle();
-	void onCheckRandomize();
-	void onChangeCycleTime();
-	
-	void updateSmart();
-	void updateCycleParameters();
-	
-	void enableSetControls(const bool enable);
-	void enableStateControls(const bool enable);
-	
-	BOOL newSetCallback(const LLSD& notification, const LLSD& response);
-	BOOL removeSetCallback(const LLSD& notification, const LLSD& response);
-	
-	virtual BOOL tick() override;
-	
-	std::vector<ALAOSet*> mSetList;
-	ALAOSet* mSelectedSet;
-	ALAOSet::AOState* mSelectedState;
-	
-	LLPanel* mReloadCoverPanel;
-	
-	// Full interface
-	
-	LLPanel* mMainInterfacePanel;
-	
-	LLComboBox* mSetSelector;
-	LLButton* mActivateSetButton;
-	LLButton* mAddButton;
-	LLButton* mRemoveButton;
-	LLCheckBoxCtrl* mDefaultCheckBox;
-	LLCheckBoxCtrl* mOverrideSitsCheckBox;
-	LLCheckBoxCtrl* mSmartCheckBox;
-	LLCheckBoxCtrl* mDisableMouselookCheckBox;
-	
-	LLComboBox* mStateSelector;
-	LLScrollListCtrl* mAnimationList;
-	LLScrollListItem* mCurrentBoldItem;
-	LLButton* mMoveUpButton;
-	LLButton* mMoveDownButton;
-	LLButton* mTrashButton;
-	LLCheckBoxCtrl* mCycleCheckBox;
-	LLCheckBoxCtrl* mRandomizeCheckBox;
-	LLTextBox* mCycleTimeTextLabel;
-	LLSpinCtrl* mCycleTimeSpinner;
-	
-	LLButton* mReloadButton;
-	
-	LLButton* mPreviousButton;
-	LLButton* mNextButton;
-	
-	bool mCanDragAndDrop;
-	bool mImportRunning;
-	
-	boost::signals2::connection mReloadCallback;
-	boost::signals2::connection mAnimationChangedCallback;
-};
-
-#endif // LL_FLOATERAO_H
diff --git a/indra/newview/alpanelaomini.cpp b/indra/newview/alpanelaomini.cpp
index d238ccef1f6..677841827a1 100644
--- a/indra/newview/alpanelaomini.cpp
+++ b/indra/newview/alpanelaomini.cpp
@@ -32,8 +32,8 @@
 #include "llviewerprecompiledheaders.h"
 #include "alpanelaomini.h"
 
-#include "alaoengine.h"
-#include "alfloaterao.h"
+#include "aoengine.h"
+#include "ao.h"
 #include "llfloaterreg.h"
 #include "llcombobox.h"
 
@@ -64,8 +64,8 @@ BOOL ALPanelAOMini::postBuild()
 {
 	mSetList = getChild<LLComboBox>("set_list");
 	mSetList->setCommitCallback(boost::bind(&ALPanelAOMini::onSelectSet, this, _2));
-	mReloadCallback = ALAOEngine::instance().setReloadCallback(boost::bind(&ALPanelAOMini::updateSetList, this));
-	mSetChangedCallback = ALAOEngine::instance().setSetChangedCallback(boost::bind(&ALPanelAOMini::onSetChanged, this, _1));
+	mReloadCallback = AOEngine::instance().setReloadCallback(boost::bind(&ALPanelAOMini::updateSetList, this));
+	mSetChangedCallback = AOEngine::instance().setSetChangedCallback(boost::bind(&ALPanelAOMini::onSetChanged, this, _1));
 	
 	return TRUE;
 }
@@ -75,17 +75,17 @@ BOOL ALPanelAOMini::postBuild()
 
 void ALPanelAOMini::updateSetList()
 {
-	const std::vector<ALAOSet*>& list = ALAOEngine::getInstance()->getSetList();
+	const std::vector<AOSet*>& list = AOEngine::getInstance()->getSetList();
 	if (list.empty())
 	{
 		return;
 	}
 	mSetList->removeall();
-	for (ALAOSet* set : list)
+	for (AOSet* set : list)
 	{
 		mSetList->add(set->getName(), &set, ADD_BOTTOM, true);
 	}
-	const std::string& current_set = ALAOEngine::instance().getCurrentSetName();
+	const std::string& current_set = AOEngine::instance().getCurrentSetName();
 	mSetList->selectByValue(LLSD(current_set));
 }
 
@@ -104,31 +104,31 @@ void ALPanelAOMini::onSetChanged(const std::string& set_name)
 
 void ALPanelAOMini::onSelectSet(const LLSD& userdata)
 {
-	ALAOSet* selected_set = ALAOEngine::instance().getSetByName(userdata.asString());
+	AOSet* selected_set = AOEngine::instance().getSetByName(userdata.asString());
 	if (selected_set)
 	{
-		ALAOEngine::instance().selectSet(selected_set);
+		AOEngine::instance().selectSet(selected_set);
 	}
 }
 
 void ALPanelAOMini::onClickSit(const LLSD& userdata)
 {
-	const std::string& current_set = ALAOEngine::instance().getCurrentSetName();
-	ALAOSet* selected_set = ALAOEngine::instance().getSetByName(current_set);
+	const std::string& current_set = AOEngine::instance().getCurrentSetName();
+	AOSet* selected_set = AOEngine::instance().getSetByName(current_set);
 	if (selected_set)
 	{
-		ALAOEngine::instance().setOverrideSits(selected_set, userdata.asBoolean());
+		AOEngine::instance().setOverrideSits(selected_set, userdata.asBoolean());
 	}
 }
 
 void ALPanelAOMini::onClickNext()
 {
-	ALAOEngine::instance().cycle(ALAOEngine::CycleNext);
+	AOEngine::instance().cycle(AOEngine::CycleNext);
 }
 
 void ALPanelAOMini::onClickPrevious()
 {
-	ALAOEngine::instance().cycle(ALAOEngine::CyclePrevious);
+	AOEngine::instance().cycle(AOEngine::CyclePrevious);
 }
 
 void ALPanelAOMini::openAOFloater()
diff --git a/indra/newview/ao.cpp b/indra/newview/ao.cpp
new file mode 100644
index 00000000000..bc5505b38cf
--- /dev/null
+++ b/indra/newview/ao.cpp
@@ -0,0 +1,904 @@
+/** 
+ * @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 "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llnotificationsutil.h"
+#include "llspinctrl.h"
+#include "llviewercontrol.h"
+#include "llviewerinventory.h"
+
+FloaterAO::FloaterAO(const LLSD& key)
+:	LLTransientDockableFloater(nullptr, true, key), LLEventTimer(10.f),
+	mSetList(0),
+	mSelectedSet(0),
+	mSelectedState(0),
+	mCanDragAndDrop(false),
+	mImportRunning(false),
+	mCurrentBoldItem(nullptr),
+	mMore(true)
+{
+	mEventTimer.stop();
+}
+
+FloaterAO::~FloaterAO()
+{
+}
+
+void FloaterAO::reloading(bool reload)
+{
+	if (reload)
+	{
+		mEventTimer.start();
+	}
+	else
+	{
+		mEventTimer.stop();
+	}
+
+	mReloadCoverPanel->setVisible(reload);
+	enableSetControls(!reload);
+	enableStateControls(!reload);
+}
+
+BOOL FloaterAO::tick()
+{
+	// reloading took too long, probably missed the signal, so we hide the reload cover
+	LL_WARNS("AOEngine") << "AO reloading timeout." << LL_ENDL;
+	updateList();
+	return FALSE;
+}
+
+void FloaterAO::updateSetParameters()
+{
+	mOverrideSitsCheckBox->setValue(mSelectedSet->getSitOverride());
+	mOverrideSitsCheckBoxSmall->setValue(mSelectedSet->getSitOverride());
+	mSmartCheckBox->setValue(mSelectedSet->getSmart());
+	mDisableMouselookCheckBox->setValue(mSelectedSet->getMouselookStandDisable());
+	BOOL isDefault = (mSelectedSet == AOEngine::instance().getDefaultSet());
+	mDefaultCheckBox->setValue(isDefault);
+	mDefaultCheckBox->setEnabled(!isDefault);
+	updateSmart();
+}
+
+void FloaterAO::updateAnimationList()
+{
+	S32 currentStateSelected = mStateSelector->getCurrentIndex();
+
+	mStateSelector->removeall();
+	onChangeAnimationSelection();
+
+	if (!mSelectedSet)
+	{
+		mStateSelector->setEnabled(FALSE);
+		mStateSelector->add(getString("ao_no_animations_loaded"));
+		return;
+	}
+
+	for (auto index = 0; index < mSelectedSet->mStateNames.size(); ++index)
+	{
+		const 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()
+{
+	mReloadButton->setEnabled(TRUE);
+	mImportRunning = false;
+
+	// Lambda provides simple Alpha sorting, note this is case sensitive.
+	auto sortRuleLambda = [](const AOSet* s1, const AOSet* s2) -> bool
+	{
+		return s1->getName() < s2->getName();
+	};
+
+	mSetList = AOEngine::instance().getSetList();
+	std::sort(mSetList.begin(), mSetList.end(), sortRuleLambda);
+
+	// remember currently selected animation set name
+	std::string currentSetName = mSetSelector->getSelectedItemLabel();
+
+	mSetSelector->removeall();
+	mSetSelectorSmall->removeall();
+	mSetSelector->clear();
+	mSetSelectorSmall->clear();
+
+	mAnimationList->deleteAllItems();
+	mCurrentBoldItem = nullptr;
+	reloading(false);
+
+	if (mSetList.empty())
+	{
+		LL_DEBUGS("AOEngine") << "empty set list" << LL_ENDL;
+		mSetSelector->add(getString("ao_no_sets_loaded"));
+		mSetSelectorSmall->add(getString("ao_no_sets_loaded"));
+		mSetSelector->selectNthItem(0);
+		mSetSelectorSmall->selectNthItem(0);
+		enableSetControls(FALSE);
+		return;
+	}
+
+	// make sure we have an animation set name to display
+	if (currentSetName.empty())
+	{
+		// selected animation set was empty, get the currently active animation set from the engine
+		currentSetName = AOEngine::instance().getCurrentSetName();
+		LL_DEBUGS("AOEngine") << "Current set name was empty, fetched name \"" << currentSetName << "\" from AOEngine" << LL_ENDL;
+
+		if(currentSetName.empty())
+		{
+			// selected animation set was empty, get the name of the first animation set in the list
+			currentSetName = mSetList[0]->getName();
+			LL_DEBUGS("AOEngine") << "Current set name still empty, fetched first set's name \"" << currentSetName << "\"" << LL_ENDL;
+		}
+	}
+
+	size_t selected_index = 0;
+	for (auto index = 0; index < mSetList.size(); ++index)
+	{
+		std::string setName = mSetList[index]->getName();
+		mSetSelector->add(setName, &mSetList[index], ADD_BOTTOM, TRUE);
+		mSetSelectorSmall->add(setName, &mSetList[index], ADD_BOTTOM, TRUE);
+		if (setName.compare(currentSetName) == 0)
+		{
+			selected_index = index;
+			mSelectedSet = AOEngine::instance().selectSetByName(currentSetName);
+			updateSetParameters();
+			updateAnimationList();
+		}
+	}
+
+	mSetSelector->selectNthItem(selected_index);
+	mSetSelectorSmall->selectNthItem(selected_index);
+
+	enableSetControls(TRUE);
+	if (mSetSelector->getSelectedItemLabel().empty())
+	{
+		onClickReload();
+	}
+}
+
+BOOL FloaterAO::postBuild()
+{
+	LLPanel* aoPanel = getChild<LLPanel>("animation_overrider_outer_panel");
+	mMainInterfacePanel = aoPanel->getChild<LLPanel>("animation_overrider_panel");
+	mSmallInterfacePanel = aoPanel->getChild<LLPanel>("animation_overrider_panel_small");
+	mReloadCoverPanel = aoPanel->getChild<LLPanel>("ao_reload_cover");
+
+	mSetSelector = mMainInterfacePanel->getChild<LLComboBox>("ao_set_selection_combo");
+	mActivateSetButton = mMainInterfacePanel->getChild<LLButton>("ao_activate");
+	mAddButton = mMainInterfacePanel->getChild<LLButton>("ao_add");
+	mRemoveButton = mMainInterfacePanel->getChild<LLButton>("ao_remove");
+	mDefaultCheckBox = mMainInterfacePanel->getChild<LLCheckBoxCtrl>("ao_default");
+	mOverrideSitsCheckBox = mMainInterfacePanel->getChild<LLCheckBoxCtrl>("ao_sit_override");
+	mSmartCheckBox = mMainInterfacePanel->getChild<LLCheckBoxCtrl>("ao_smart");
+	mDisableMouselookCheckBox = mMainInterfacePanel->getChild<LLCheckBoxCtrl>("ao_disable_stands_in_mouselook");
+
+	mStateSelector = mMainInterfacePanel->getChild<LLComboBox>("ao_state_selection_combo");
+	mAnimationList = mMainInterfacePanel->getChild<LLScrollListCtrl>("ao_state_animation_list");
+	mMoveUpButton = mMainInterfacePanel->getChild<LLButton>("ao_move_up");
+	mMoveDownButton = mMainInterfacePanel->getChild<LLButton>("ao_move_down");
+	mTrashButton = mMainInterfacePanel->getChild<LLButton>("ao_trash");
+	mCycleCheckBox = mMainInterfacePanel->getChild<LLCheckBoxCtrl>("ao_cycle");
+	mRandomizeCheckBox = mMainInterfacePanel->getChild<LLCheckBoxCtrl>("ao_randomize");
+	mCycleTimeTextLabel = mMainInterfacePanel->getChild<LLTextBox>("ao_cycle_time_seconds_label");
+	mCycleTimeSpinner = mMainInterfacePanel->getChild<LLSpinCtrl>("ao_cycle_time");
+
+	mReloadButton = mMainInterfacePanel->getChild<LLButton>("ao_reload");
+	mPreviousButton = mMainInterfacePanel->getChild<LLButton>("ao_previous");
+	mNextButton = mMainInterfacePanel->getChild<LLButton>("ao_next");
+	mLessButton = mMainInterfacePanel->getChild<LLButton>("ao_less");
+
+	mSetSelectorSmall = mSmallInterfacePanel->getChild<LLComboBox>("ao_set_selection_combo_small");
+	mMoreButton = mSmallInterfacePanel->getChild<LLButton>("ao_more");
+	mPreviousButtonSmall = mSmallInterfacePanel->getChild<LLButton>("ao_previous_small");
+	mNextButtonSmall = mSmallInterfacePanel->getChild<LLButton>("ao_next_small");
+	mOverrideSitsCheckBoxSmall = mSmallInterfacePanel->getChild<LLCheckBoxCtrl>("ao_sit_override_small");
+
+	mSetSelector->setCommitCallback(boost::bind(&FloaterAO::onSelectSet, this));
+	mSetSelector->setFocusLostCallback(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));
+	mSmartCheckBox->setCommitCallback(boost::bind(&FloaterAO::onCheckSmart, 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));
+	mCycleCheckBox->setCommitCallback(boost::bind(&FloaterAO::onCheckCycle, this));
+	mRandomizeCheckBox->setCommitCallback(boost::bind(&FloaterAO::onCheckRandomize, this));
+	mCycleTimeSpinner->setCommitCallback(boost::bind(&FloaterAO::onChangeCycleTime, this));
+
+	mReloadButton->setCommitCallback(boost::bind(&FloaterAO::onClickReload, this));
+	mPreviousButton->setCommitCallback(boost::bind(&FloaterAO::onClickPrevious, this));
+	mNextButton->setCommitCallback(boost::bind(&FloaterAO::onClickNext, this));
+	mLessButton->setCommitCallback(boost::bind(&FloaterAO::onClickLess, this));
+	mOverrideSitsCheckBoxSmall->setCommitCallback(boost::bind(&FloaterAO::onCheckOverrideSitsSmall, this));
+
+	mSetSelectorSmall->setCommitCallback(boost::bind(&FloaterAO::onSelectSetSmall, this));
+	mMoreButton->setCommitCallback(boost::bind(&FloaterAO::onClickMore, this));
+	mPreviousButtonSmall->setCommitCallback(boost::bind(&FloaterAO::onClickPrevious, this));
+	mNextButtonSmall->setCommitCallback(boost::bind(&FloaterAO::onClickNext, this));
+
+	updateSmart();
+
+	AOEngine::instance().setReloadCallback(boost::bind(&FloaterAO::updateList, this));
+	AOEngine::instance().setAnimationChangedCallback(boost::bind(&FloaterAO::onAnimationChanged, this, _1));
+
+	onChangeAnimationSelection();
+	mMainInterfacePanel->setVisible(TRUE);
+	mSmallInterfacePanel->setVisible(FALSE);
+	reloading(true);
+
+	updateList();
+
+	if (gSavedPerAccountSettings.getBOOL("UseFullAOInterface"))
+	{
+		onClickMore();
+	}
+	else
+	{
+		onClickLess();
+	}
+
+	return LLDockableFloater::postBuild();
+}
+
+void FloaterAO::enableSetControls(BOOL enable)
+{
+	mSetSelector->setEnabled(enable);
+	mSetSelectorSmall->setEnabled(enable);
+	mActivateSetButton->setEnabled(enable);
+	mRemoveButton->setEnabled(enable);
+	mDefaultCheckBox->setEnabled(enable && (mSelectedSet != AOEngine::instance().getDefaultSet()));
+	mOverrideSitsCheckBox->setEnabled(enable);
+	mOverrideSitsCheckBoxSmall->setEnabled(enable);
+	mDisableMouselookCheckBox->setEnabled(enable);
+
+	if (!enable)
+	{
+		enableStateControls(enable);
+	}
+}
+
+void FloaterAO::enableStateControls(BOOL enable)
+{
+	mStateSelector->setEnabled(enable);
+	mAnimationList->setEnabled(enable);
+	mCycleCheckBox->setEnabled(enable);
+	if (enable)
+	{
+		updateCycleParameters();
+	}
+	else
+	{
+		mRandomizeCheckBox->setEnabled(enable);
+		mCycleTimeTextLabel->setEnabled(enable);
+		mCycleTimeSpinner->setEnabled(enable);
+	}
+	mPreviousButton->setEnabled(enable);
+	mPreviousButtonSmall->setEnabled(enable);
+	mNextButton->setEnabled(enable);
+	mNextButtonSmall->setEnabled(enable);
+	mCanDragAndDrop = enable;
+}
+
+void FloaterAO::onOpen(const LLSD& key)
+{
+	LLDockableFloater::onOpen(key);
+}
+
+void FloaterAO::onClose(bool app_quitting)
+{
+	LLDockableFloater::onClose(app_quitting);
+}
+
+void FloaterAO::onSelectSet()
+{
+	AOSet* set = AOEngine::instance().getSetByName(mSetSelector->getSelectedItemLabel());
+	if (!set)
+	{
+		onRenameSet();
+		return;
+	}
+	
+	// only update the interface when we actually selected a different set - FIRE-29542
+	if (mSelectedSet != set)
+	{
+		mSelectedSet=set;
+
+		updateSetParameters();
+		updateAnimationList();
+	}
+}
+
+void FloaterAO::onSelectSetSmall()
+{
+	// sync main set selector with small set selector
+	mSetSelector->selectNthItem(mSetSelectorSmall->getCurrentIndex());
+
+	mSelectedSet = AOEngine::instance().getSetByName(mSetSelectorSmall->getSelectedItemLabel());
+	if (mSelectedSet)
+	{
+		updateSetParameters();
+		updateAnimationList();
+
+		// small selector activates the selected set immediately
+		onClickActivate();
+	}
+}
+
+void FloaterAO::onRenameSet()
+{
+	if (!mSelectedSet)
+	{
+		LL_WARNS("AOEngine") << "Rename AO set without set selected." << LL_ENDL;
+		return;
+	}
+
+	std::string name = mSetSelector->getSimple();
+	LLStringUtil::trim(name);
+
+	LLUIString new_set_name = name;
+
+	if (!name.empty())
+	{
+		if (
+			LLTextValidate::validateASCIIPrintableNoPipe(new_set_name.getWString()) &&	// only allow ASCII
+			name.find_first_of(":|") == std::string::npos)								// don't allow : or |
+		{
+			if (AOEngine::instance().renameSet(mSelectedSet, name))
+			{
+				reloading(TRUE);
+				return;
+			}
+		}
+		else
+		{
+			LLSD args;
+			args["AO_SET_NAME"] = name;
+			LLNotificationsUtil::add("RenameAOMustBeASCII", args);
+		}
+	}
+	mSetSelector->setSimple(mSelectedSet->getName());
+}
+
+void FloaterAO::onClickActivate()
+{
+	// sync small set selector with main set selector
+	mSetSelectorSmall->selectNthItem(mSetSelector->getCurrentIndex());
+
+	LL_DEBUGS("AOEngine") << "Set activated: " << mSetSelector->getSelectedItemLabel() << LL_ENDL;
+	AOEngine::instance().selectSet(mSelectedSet);
+}
+
+LLScrollListItem* FloaterAO::addAnimation(const std::string& name)
+{
+	LLSD row;
+	row["columns"][0]["column"] = "icon";
+	row["columns"][0]["type"] = "icon";
+	row["columns"][0]["value"] = "Stop_Off";
+
+	row["columns"][1]["column"] = "animation_name";
+	row["columns"][1]["type"] = "text";
+	row["columns"][1]["value"] = name;
+
+	return mAnimationList->addElement(row);
+}
+
+void FloaterAO::onSelectState()
+{
+	mAnimationList->deleteAllItems();
+	mCurrentBoldItem = nullptr;
+	mAnimationList->setCommentText(getString("ao_no_animations_loaded"));
+	mAnimationList->setEnabled(FALSE);
+
+	onChangeAnimationSelection();
+
+	if (!mSelectedSet)
+	{
+		return;
+	}
+
+	mSelectedState = mSelectedSet->getStateByName(mStateSelector->getSelectedItemLabel());
+	if (!mSelectedState)
+	{
+		return;
+	}
+
+	mSelectedState = (AOSet::AOState*)mStateSelector->getCurrentUserdata();
+	if (mSelectedState->mAnimations.size())
+	{
+		for (auto index = 0; index < mSelectedState->mAnimations.size(); ++index)
+		{
+			LLScrollListItem* item = addAnimation(mSelectedState->mAnimations[index].mName);
+			if (item)
+			{
+				item->setUserdata(&mSelectedState->mAnimations[index].mInventoryUUID);
+
+				// update currently playing animation if we are looking at the currently running state in the UI
+				if (mSelectedSet->getMotion() == mSelectedState->mRemapID &&
+				    mSelectedState->mCurrentAnimationID == mSelectedState->mAnimations[index].mAssetUUID)
+				{
+					mCurrentBoldItem = item;
+					((LLScrollListIcon*)item->getColumn(0))->setValue("Play_Over");
+					((LLScrollListText*)item->getColumn(1))->setFontStyle(LLFontGL::BOLD);
+				}
+			}
+		}
+
+		mAnimationList->setCommentText("");
+		mAnimationList->setEnabled(TRUE);
+	}
+
+	mCycleCheckBox->setValue(mSelectedState->mCycle);
+	mRandomizeCheckBox->setValue(mSelectedState->mRandom);
+	mCycleTimeSpinner->setValue(mSelectedState->mCycleTime);
+
+	updateCycleParameters();
+}
+
+void FloaterAO::onClickReload()
+{
+	reloading(true);
+
+	mSelectedSet = nullptr;
+	mSelectedState = nullptr;
+
+	AOEngine::instance().reload(false);
+	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);
+
+	LLStringUtil::trim(newSetName);
+
+	LLUIString new_set_name = newSetName;
+
+	if (newSetName.empty())
+	{
+		return false;
+	}
+	else if (
+		!LLTextValidate::validateASCIIPrintableNoPipe(new_set_name.getWString()) ||		// only allow ASCII
+		newSetName.find_first_of(":|") != std::string::npos)							// don't allow : or |
+	{
+		LLSD args;
+		args["AO_SET_NAME"] = newSetName;
+		LLNotificationsUtil::add("NewAOCantContainNonASCII", args);
+		return false;
+	}
+
+	if (option == 0)
+	{
+		AOEngine::instance().addSet(newSetName, [this](const LLUUID& new_cat_id)
+		{
+			reloading(true);
+		});
+	}
+	return false;
+}
+
+void FloaterAO::onClickRemove()
+{
+	if (!mSelectedSet)
+	{
+		return;
+	}
+
+	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)
+{
+	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+
+	if (option ==0)
+	{
+		if (AOEngine::instance().removeSet(mSelectedSet))
+		{
+			reloading(true);
+			// to prevent snapping back to deleted set
+			mSetSelector->removeall();
+			mSetSelectorSmall->removeall();
+			// visually indicate there are no items left
+			mSetSelector->clear();
+			mSetSelectorSmall->clear();
+			mAnimationList->deleteAllItems();
+			mCurrentBoldItem = nullptr;
+			return true;
+		}
+	}
+	return false;
+}
+
+void FloaterAO::onCheckDefault()
+{
+	if (mSelectedSet)
+	{
+		AOEngine::instance().setDefaultSet(mSelectedSet);
+	}
+}
+
+void FloaterAO::onCheckOverrideSits()
+{
+	mOverrideSitsCheckBoxSmall->setValue(mOverrideSitsCheckBox->getValue());
+	if (mSelectedSet)
+	{
+		AOEngine::instance().setOverrideSits(mSelectedSet, mOverrideSitsCheckBox->getValue().asBoolean());
+	}
+	updateSmart();
+}
+
+void FloaterAO::onCheckOverrideSitsSmall()
+{
+	mOverrideSitsCheckBox->setValue(mOverrideSitsCheckBoxSmall->getValue());
+	onCheckOverrideSits();
+}
+
+void FloaterAO::updateSmart()
+{
+	mSmartCheckBox->setEnabled(mOverrideSitsCheckBox->getValue());
+}
+
+void FloaterAO::onCheckSmart()
+{
+	if (mSelectedSet)
+	{
+		AOEngine::instance().setSmart(mSelectedSet, mSmartCheckBox->getValue().asBoolean());
+	}
+}
+
+void FloaterAO::onCheckDisableStands()
+{
+	if (mSelectedSet)
+	{
+		AOEngine::instance().setDisableMouselookStands(mSelectedSet, mDisableMouselookCheckBox->getValue().asBoolean());
+	}
+}
+
+void FloaterAO::onChangeAnimationSelection()
+{
+	std::vector<LLScrollListItem*> list = mAnimationList->getAllSelected();
+	LL_DEBUGS("AOEngine") << "Selection count: " << list.size() << LL_ENDL;
+
+	BOOL resortEnable = FALSE;
+	BOOL trashEnable = FALSE;
+
+	// Linden Lab bug: scroll lists still select the first item when you click on them, even when they are disabled.
+	// The control does not memorize it's enabled/disabled state, so mAnimationList->mEnabled() doesn't seem to work.
+	// So we need to safeguard against it.
+	if (!mCanDragAndDrop)
+	{
+		mAnimationList->deselectAllItems();
+		LL_DEBUGS("AOEngine") << "Selection count now: " << list.size() << LL_ENDL;
+	}
+	else if (!list.empty())
+	{
+		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<LLScrollListItem*> 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<LLScrollListItem*> 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<LLScrollListItem*> list = mAnimationList->getAllSelected();
+	if (list.empty())
+	{
+		return;
+	}
+
+	for (auto index = list.size() - 1; index != -1; --index)
+	{
+		AOEngine::instance().removeAnimation(mSelectedSet, mSelectedState, mAnimationList->getItemIndex(list[index]));
+	}
+
+	mAnimationList->deleteSelectedItems();
+	mCurrentBoldItem = nullptr;
+}
+
+void FloaterAO::updateCycleParameters()
+{
+	BOOL enabled = mCycleCheckBox->getValue().asBoolean();
+	mRandomizeCheckBox->setEnabled(enabled);
+	mCycleTimeTextLabel->setEnabled(enabled);
+	mCycleTimeSpinner->setEnabled(enabled);
+}
+
+void FloaterAO::onCheckCycle()
+{
+	if (mSelectedState)
+	{
+		bool cycle = mCycleCheckBox->getValue().asBoolean();
+		AOEngine::instance().setCycle(mSelectedState, cycle);
+		updateCycleParameters();
+	}
+}
+
+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);
+}
+
+void FloaterAO::onClickMore()
+{
+	LLRect fullSize = gSavedPerAccountSettings.getRect("floater_rect_ao_full");
+
+	if (fullSize.getHeight() < getMinHeight())
+	{
+		fullSize.setOriginAndSize(fullSize.mLeft, fullSize.mBottom, fullSize.getWidth(), getRect().getHeight());
+	}
+
+	if (fullSize.getWidth() < getMinWidth())
+	{
+		fullSize.setOriginAndSize(fullSize.mLeft, fullSize.mBottom, getRect().getWidth(), fullSize.getHeight());
+	}
+
+	mMore = true;
+
+	mSmallInterfacePanel->setVisible(FALSE);
+	mMainInterfacePanel->setVisible(TRUE);
+	enableResizeCtrls(true, true, true);
+
+	gSavedPerAccountSettings.setBOOL("UseFullAOInterface", TRUE);
+
+	reshape(getRect().getWidth(), fullSize.getHeight());
+}
+
+void FloaterAO::onClickLess()
+{
+	LLRect fullSize = getRect();
+	LLRect smallSize = mSmallInterfacePanel->getRect();
+	smallSize.setLeftTopAndSize(0, 0, smallSize.getWidth(), smallSize.getHeight() + getHeaderHeight());
+
+	gSavedPerAccountSettings.setRect("floater_rect_ao_full", fullSize);
+
+	mMore = false;
+
+	mSmallInterfacePanel->setVisible(TRUE);
+	mMainInterfacePanel->setVisible(FALSE);
+	enableResizeCtrls(true, true, false);
+
+	gSavedPerAccountSettings.setBOOL("UseFullAOInterface", FALSE);
+
+	reshape(getRect().getWidth(), smallSize.getHeight());
+
+	// save current size and position
+	gSavedPerAccountSettings.setRect("floater_rect_ao_full", fullSize);
+}
+
+void FloaterAO::onAnimationChanged(const LLUUID& animation)
+{
+	LL_DEBUGS("AOEngine") << "Received animation change to " << animation << LL_ENDL;
+
+	if (mCurrentBoldItem)
+	{
+		((LLScrollListIcon*)mCurrentBoldItem->getColumn(0))->setValue("Stop_Off");
+		((LLScrollListText*)mCurrentBoldItem->getColumn(1))->setFontStyle(LLFontGL::NORMAL);
+
+		mCurrentBoldItem = nullptr;
+	}
+
+	if (animation.isNull())
+	{
+		return;
+	}
+
+	// why do we have no LLScrollListCtrl::getItemByUserdata() ? -Zi
+	for (auto item : mAnimationList->getAllData())
+	{
+		LLUUID* id = (LLUUID*)item->getUserdata();
+
+		if (id == &animation)
+		{
+			mCurrentBoldItem = item;
+
+			((LLScrollListIcon*)mCurrentBoldItem->getColumn(0))->setValue("Play_Over");
+			((LLScrollListText*)mCurrentBoldItem->getColumn(1))->setFontStyle(LLFontGL::BOLD);
+
+			return;
+		}
+	}
+}
+
+// virtual
+BOOL FloaterAO::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType type, void* data,
+									EAcceptance* accept, std::string& tooltipMsg)
+{
+	// no drag & drop on small interface
+	if (!mMore)
+	{
+		tooltipMsg = getString("ao_dnd_only_on_full_interface");
+		*accept = ACCEPT_NO;
+		return TRUE;
+	}
+
+	LLInventoryItem* item = (LLInventoryItem*)data;
+
+	if (type == DAD_NOTECARD)
+	{
+		if (mImportRunning)
+		{
+			*accept = ACCEPT_NO;
+			return TRUE;
+		}
+		*accept = ACCEPT_YES_SINGLE;
+		if (item && drop)
+		{
+			if (AOEngine::instance().importNotecard(item))
+			{
+				reloading(TRUE);
+				mReloadButton->setEnabled(FALSE);
+				mImportRunning = TRUE;
+			}
+		}
+	}
+	else if (type == DAD_ANIMATION)
+	{
+		if (!drop && (!mSelectedSet || !mSelectedState || !mCanDragAndDrop))
+		{
+			*accept = ACCEPT_NO;
+			return TRUE;
+		}
+		*accept = ACCEPT_YES_MULTI;
+		if (item && drop)
+		{
+			AOEngine::instance().addAnimation(mSelectedSet, mSelectedState, item);
+			addAnimation(item->getName());
+
+			// TODO: this would be the right thing to do, but it blocks multi drop
+			// before final release this must be resolved
+			reloading(true);
+		}
+	}
+	else
+	{
+		*accept = ACCEPT_NO;
+	}
+
+	return TRUE;
+}
diff --git a/indra/newview/ao.h b/indra/newview/ao.h
new file mode 100644
index 00000000000..45482bab453
--- /dev/null
+++ b/indra/newview/ao.h
@@ -0,0 +1,159 @@
+/** 
+ * @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 "lleventtimer.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,
+	public LLEventTimer
+{
+	friend class LLFloaterReg;
+
+	private:
+		FloaterAO(const LLSD& key);
+		~FloaterAO();
+
+	public:
+		/*virtual*/ BOOL postBuild();
+		virtual void onOpen(const LLSD& key);
+		virtual void onClose(bool app_quitting);
+		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 onSelectSetSmall();
+		void onRenameSet();
+		void onSelectState();
+		void onChangeAnimationSelection();
+		void onClickReload();
+		void onClickAdd();
+		void onClickRemove();
+		void onClickActivate();
+		void onCheckDefault();
+		void onCheckOverrideSits();
+		void onCheckOverrideSitsSmall();
+		void onCheckSmart();
+		void onCheckDisableStands();
+		void onClickMoveUp();
+		void onClickMoveDown();
+		void onClickTrash();
+		void onCheckCycle();
+		void onCheckRandomize();
+		void onChangeCycleTime();
+		void onClickPrevious();
+		void onClickNext();
+
+		void onClickMore();
+		void onClickLess();
+
+		void onAnimationChanged(const LLUUID& animation);
+
+		void reloading(bool reload);
+
+		void updateSmart();
+		void updateCycleParameters();
+
+		void enableSetControls(BOOL enable);
+		void enableStateControls(BOOL enable);
+
+		bool newSetCallback(const LLSD& notification, const LLSD& response);
+		bool removeSetCallback(const LLSD& notification, const LLSD& response);
+
+		virtual BOOL tick();
+
+		std::vector<AOSet*> mSetList;
+		AOSet* mSelectedSet;
+		AOSet::AOState* mSelectedState;
+
+		LLPanel* mReloadCoverPanel;
+
+		// Full interface
+
+		LLPanel* mMainInterfacePanel;
+
+		LLComboBox* mSetSelector;
+		LLButton* mActivateSetButton;
+		LLButton* mAddButton;
+		LLButton* mRemoveButton;
+		LLCheckBoxCtrl* mDefaultCheckBox;
+		LLCheckBoxCtrl* mOverrideSitsCheckBox;
+		LLCheckBoxCtrl* mSmartCheckBox;
+		LLCheckBoxCtrl* mDisableMouselookCheckBox;
+
+		LLComboBox* mStateSelector;
+		LLScrollListCtrl* mAnimationList;
+		LLScrollListItem* mCurrentBoldItem;
+		LLButton* mMoveUpButton;
+		LLButton* mMoveDownButton;
+		LLButton* mTrashButton;
+		LLCheckBoxCtrl* mCycleCheckBox;
+		LLCheckBoxCtrl* mRandomizeCheckBox;
+		LLTextBox* mCycleTimeTextLabel;
+		LLSpinCtrl* mCycleTimeSpinner;
+
+		LLButton* mReloadButton;
+
+		LLButton* mPreviousButton;
+		LLButton* mNextButton;
+		LLButton* mLessButton;
+
+		// Small interface
+
+		LLPanel* mSmallInterfacePanel;
+
+		LLComboBox* mSetSelectorSmall;
+		LLButton* mMoreButton;
+		LLButton* mPreviousButtonSmall;
+		LLButton* mNextButtonSmall;
+		LLCheckBoxCtrl* mOverrideSitsCheckBoxSmall;
+
+		bool mCanDragAndDrop;
+		bool mImportRunning;
+		bool mMore;
+};
+
+#endif // AO_H
diff --git a/indra/newview/aoengine.cpp b/indra/newview/aoengine.cpp
new file mode 100644
index 00000000000..683a54f7e1e
--- /dev/null
+++ b/indra/newview/aoengine.cpp
@@ -0,0 +1,2446 @@
+/**
+ * @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
+ * $/LicenseInfo$
+ */
+
+#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 "llfilesystem.h"
+#include "llinventoryfunctions.h"		// for ROOT_FIRESTORM_FOLDER
+#include "llinventorymodel.h"
+#include "llnotificationsutil.h"
+#include "llstring.h"
+#include "llviewercontrol.h"
+#include "llviewerobjectlist.h"
+#include "llvoavatarself.h"
+
+constexpr F32 INVENTORY_POLLING_INTERVAL = 5.0f;
+
+AOEngine::AOEngine() :
+	LLSingleton<AOEngine>(),
+	mCurrentSet(nullptr),
+	mDefaultSet(nullptr),
+	mEnabled(false),
+	mEnabledStands(false),
+	mInMouselook(false),
+	mUnderWater(false),
+	mImportSet(nullptr),
+	mAOFolder(LLUUID::null),
+	mLastMotion(ANIM_AGENT_STAND),
+	mLastOverriddenMotion(ANIM_AGENT_STAND)
+{
+	gSavedPerAccountSettings.getControl("AlchemyAOEnable")->getCommitSignal()->connect(boost::bind(&AOEngine::onToggleAOControl, this));
+	gSavedPerAccountSettings.getControl("UseAOStands")->getCommitSignal()->connect(boost::bind(&AOEngine::onToggleAOStandsControl, this));
+	gSavedPerAccountSettings.getControl("PauseAO")->getCommitSignal()->connect(boost::bind(&AOEngine::onPauseAO, this));
+
+	mRegionChangeConnection = gAgent.addRegionChangedCallback(boost::bind(&AOEngine::onRegionChange, this));
+}
+
+AOEngine::~AOEngine()
+{
+	clear(false);
+
+	if (mRegionChangeConnection.connected())
+	{
+		mRegionChangeConnection.disconnect();
+	}
+}
+
+void AOEngine::init()
+{
+	BOOL do_enable = gSavedPerAccountSettings.getBOOL("AlchemyAOEnable");
+	BOOL do_enable_stands = gSavedPerAccountSettings.getBOOL("UseAOStands");
+	if (do_enable)
+	{
+		// enable_stands() calls enable(), but we need to set the
+		// mEnabled variable properly
+		mEnabled = true;
+		// Enabling the AO always enables stands to start with
+		enableStands(true);
+	}
+	else
+	{
+		enableStands(do_enable_stands);
+		enable(false);
+	}
+}
+
+// static
+void AOEngine::onLoginComplete()
+{
+	AOEngine::instance().init();
+}
+
+void AOEngine::onToggleAOControl()
+{
+	enable(gSavedPerAccountSettings.getBOOL("AlchemyAOEnable"));
+	if (mEnabled)
+	{
+		// Enabling the AO always enables stands to start with
+		gSavedPerAccountSettings.setBOOL("UseAOStands", TRUE);
+	}
+}
+
+void AOEngine::onToggleAOStandsControl()
+{
+	enableStands(gSavedPerAccountSettings.getBOOL("UseAOStands"));
+}
+
+void AOEngine::onPauseAO()
+{
+	// can't use mEnabled here as that gets switched over by enable()
+	if (gSavedPerAccountSettings.getBOOL("AlchemyAOEnable"))
+	{
+		enable(!gSavedPerAccountSettings.getBOOL("PauseAO"));
+	}
+}
+
+void AOEngine::clear(bool from_timer)
+{
+	std::move(mSets.begin(), mSets.end(), std::back_inserter(mOldSets));
+	mSets.clear();
+
+	mCurrentSet = nullptr;
+	mSetChangedSignal(mCurrentSet->getName());
+
+	//<ND/> FIRE-3801; We cannot delete any AOSet object if we're called from a timer tick. AOSet is derived from LLEventTimer and destruction will
+	// fail in ~LLInstanceTracker when a destructor runs during iteration.
+	if (!from_timer)
+	{
+		std::for_each(mOldSets.begin(), mOldSets.end(), DeletePointer());
+		mOldSets.clear();
+
+		std::for_each(mOldImportSets.begin(), mOldImportSets.end(), DeletePointer());
+		mOldImportSets.clear();
+	}
+}
+
+void AOEngine::stopAllStandVariants()
+{
+	LL_DEBUGS("AOEngine") << "stopping all STAND variants." << LL_ENDL;
+	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()
+{
+	LL_DEBUGS("AOEngine") << "stopping all SIT variants." << LL_ENDL;
+	gAgent.sendAnimationRequest(ANIM_AGENT_SIT_FEMALE, ANIM_REQUEST_STOP);
+	gAgent.sendAnimationRequest(ANIM_AGENT_SIT_GENERIC, ANIM_REQUEST_STOP);
+	gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_SIT_FEMALE);
+	gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_SIT_GENERIC);
+
+	// scripted seats that use ground_sit as animation need special treatment
+	const LLViewerObject* agentRoot = dynamic_cast<LLViewerObject*>(gAgentAvatarp->getRoot());
+	if (agentRoot && agentRoot->getID() != gAgentID)
+	{
+		LL_DEBUGS("AOEngine") << "Not stopping ground sit animations while sitting on a prim." << LL_ENDL;
+		return;
+	}
+
+	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_GROUND);
+	gAgentAvatarp->LLCharacter::stopMotion(ANIM_AGENT_SIT_GROUND_CONSTRAINED);
+}
+
+void AOEngine::setLastMotion(const LLUUID& motion)
+{
+	if (motion != ANIM_AGENT_TYPE)
+	{
+		mLastMotion = motion;
+	}
+}
+
+void AOEngine::setLastOverriddenMotion(const LLUUID& motion)
+{
+	if (motion != ANIM_AGENT_TYPE)
+	{
+		mLastOverriddenMotion = motion;
+	}
+}
+
+bool AOEngine::foreignAnimations()
+{
+	// checking foreign animations only makes sense when smart sit is enabled
+	if (!mCurrentSet->getSmart())
+	{
+		return false;
+	}
+
+	// get the seat the avatar is sitting on
+	const LLViewerObject* agentRoot = dynamic_cast<LLViewerObject*>(gAgentAvatarp->getRoot());
+	if (!agentRoot)
+	{
+		// this should not happen, ever
+		return false;
+	}
+
+	const LLUUID& seat = agentRoot->getID();
+	if (seat == gAgentID)
+	{
+		LL_DEBUGS("AOEngine") << "Not checking for foreign animation when not sitting." << LL_ENDL;
+		return false;
+	}
+
+	LL_DEBUGS("AOEngine") << "Checking for foreign animation on seat " << seat << LL_ENDL;
+
+	for (const auto& [source_id, animation_id] : gAgentAvatarp->mAnimationSources)
+	{
+		// skip animations run by the avatar itself
+		if (source_id != gAgentID)
+		{
+			// find the source object where the animation came from
+			LLViewerObject* source = gObjectList.findObject(source_id);
+
+			// proceed if it's not an attachment
+			if (source && !source->isAttachment())
+			{
+				LL_DEBUGS("AOEngine") << "Source " << source_id << " is running animation " << animation_id << LL_ENDL;
+
+				// get the source's root prim
+				LLViewerObject* sourceRoot = dynamic_cast<LLViewerObject*>(source->getRoot());
+
+				// if the root prim is the same as the animation source, report back as TRUE
+				if (sourceRoot && sourceRoot->getID() == seat)
+				{
+					LL_DEBUGS("AOEngine") << "foreign animation " << animation_id << " found on seat." << LL_ENDL;
+					return true;
+				}
+			}
+		}
+	}
+
+	return false;
+}
+
+// map motion to underwater state, return nullptr if not applicable
+AOSet::AOState* AOEngine::mapSwimming(const LLUUID& motion) const
+{
+	S32 stateNum;
+
+	if (motion == ANIM_AGENT_HOVER)
+	{
+		stateNum = AOSet::Floating;
+	}
+	else if (motion == ANIM_AGENT_FLY)
+	{
+		stateNum = AOSet::SwimmingForward;
+	}
+	else if (motion == ANIM_AGENT_HOVER_UP)
+	{
+		stateNum = AOSet::SwimmingUp;
+	}
+	else if (motion == ANIM_AGENT_HOVER_DOWN)
+	{
+		stateNum = AOSet::SwimmingDown;
+	}
+	else
+	{
+		// motion not applicable for underwater mapping
+		return nullptr;
+	}
+
+	return mCurrentSet->getState(stateNum);
+}
+
+// switch between swimming and flying on transition in and out of Linden region water
+void AOEngine::checkBelowWater(bool check_underwater)
+{
+	// there was no transition, do nothing
+	if (mUnderWater == check_underwater)
+	{
+		return;
+	}
+
+	// only applies to motions that actually change underwater and have animations inside
+	AOSet::AOState* mapped = mapSwimming(mLastMotion);
+	if (!mapped || mapped->mAnimations.empty())
+	{
+		// set underwater status but do nothing else
+		mUnderWater = check_underwater;
+		return;
+	}
+
+	// find animation id to stop when transitioning
+	LLUUID id = override(mLastMotion, false);
+	if (id.isNull())
+	{
+		// no animation in overrider for this state, use Linden Lab motion
+		id = mLastMotion;
+	}
+
+	// stop currently running animation
+	gAgent.sendAnimationRequest(id, ANIM_REQUEST_STOP);
+
+	if (!mUnderWater)
+	{
+		// remember which animation we stopped while going under water, to catch the stop
+		// request later in the overrider - this prevents the overrider from triggering itself
+		// after the region comes back with the stop request for Linden Lab motion ids
+		mTransitionId = id;
+	}
+
+	// set requested underwater status for overrider
+	mUnderWater = check_underwater;
+
+	// find animation id to start when transitioning
+	id = override(mLastMotion, true);
+	if (id.isNull())
+	{
+		// no animation in overrider for this state, use Linden Lab motion
+		id = mLastMotion;
+	}
+
+	// start new animation
+	gAgent.sendAnimationRequest(id, ANIM_REQUEST_START);
+}
+
+// find the correct animation state for the requested motion, mapping flying to
+// swimming where necessary
+AOSet::AOState* AOEngine::getStateForMotion(const LLUUID& motion) const
+{
+	// get default state for this motion
+	AOSet::AOState* defaultState = mCurrentSet->getStateByRemapID(motion);
+	if (!mUnderWater)
+	{
+		return defaultState;
+	}
+
+	// get state for underwater motion
+	AOSet::AOState* mapped = mapSwimming(motion);
+	if (!mapped)
+	{
+		// not applicable for underwater motion, so use default state
+		return defaultState;
+	}
+
+	// check if the underwater state has any animations to play
+	if (mapped->mAnimations.empty())
+	{
+		// no animations in underwater state, return default
+		return defaultState;
+	}
+	return mapped;
+}
+
+void AOEngine::enableStands(bool enable_stands)
+{
+	mEnabledStands = enable_stands;
+	// let the main enable routine decide if we need to change animations
+	// but don't actually change the state of the enabled flag
+	enable(mEnabled);
+}
+
+void AOEngine::enable(bool enable)
+{
+	LL_DEBUGS("AOEngine") << "using " << gAnimLibrary.animationName(mLastMotion) << " enable " << enable << LL_ENDL;
+	mEnabled = enable;
+
+	if (!mCurrentSet)
+	{
+		LL_DEBUGS("AOEngine") << "enable(" << enable << ") without animation set loaded." << LL_ENDL;
+		return;
+	}
+
+	AOSet::AOState* state = mCurrentSet->getStateByRemapID(mLastMotion);
+	if (mEnabled)
+	{
+		if (state && !state->mAnimations.empty())
+		{
+			LL_DEBUGS("AOEngine") << "Enabling animation state " << state->mName << LL_ENDL;
+
+			// do not stop underlying sit animations when re-enabling the AO
+			if (mLastOverriddenMotion != ANIM_AGENT_SIT_GROUND &&
+			    mLastOverriddenMotion != ANIM_AGENT_SIT_GROUND_CONSTRAINED &&
+			    mLastOverriddenMotion != ANIM_AGENT_SIT)
+			{
+				gAgent.sendAnimationRequest(mLastOverriddenMotion, ANIM_REQUEST_STOP);
+			}
+
+			LLUUID animation = override(mLastMotion, true);
+			if (animation.isNull())
+			{
+				return;
+			}
+
+			if (mLastMotion == ANIM_AGENT_STAND)
+			{
+				if (!mEnabledStands)
+				{
+					LL_DEBUGS("AOEngine") << "Last motion was a STAND, but disabled for stands, ignoring." << LL_ENDL;
+					return;
+				}
+				stopAllStandVariants();
+			}
+			else if (mLastMotion == ANIM_AGENT_WALK)
+			{
+				LL_DEBUGS("AOEngine") << "Last motion was a WALK, stopping all variants." << LL_ENDL;
+				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)
+			{
+				LL_DEBUGS("AOEngine") << "Last motion was a RUN, stopping all variants." << LL_ENDL;
+				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();
+				gAgent.sendAnimationRequest(ANIM_AGENT_SIT_GENERIC, ANIM_REQUEST_START);
+			}
+			else
+			{
+				LL_WARNS("AOEngine") << "Unhandled last motion id " << gAnimLibrary.animationName(mLastMotion) << LL_ENDL;
+			}
+
+			gAgent.sendAnimationRequest(animation, ANIM_REQUEST_START);
+			mAnimationChangedSignal(state->mAnimations[state->mCurrentAnimation].mInventoryUUID);
+
+			// remember to ignore this motion once in the overrider so stopping the Linden motion
+			// will not trigger a stop of the override animation
+			mIgnoreMotionStopOnce = mLastMotion;
+		}
+	}
+	else
+	{
+		mAnimationChangedSignal(LLUUID::null);
+
+		if (mLastOverriddenMotion == ANIM_AGENT_SIT)
+		{
+			// remove sit cycle cover up
+			gAgent.sendAnimationRequest(ANIM_AGENT_SIT_GENERIC, ANIM_REQUEST_STOP);
+		}
+
+		// stop all overriders, catch leftovers
+		for (S32 index = 0; index < AOSet::AOSTATES_MAX; ++index)
+		{
+			state = mCurrentSet->getState(index);
+			if (state)
+			{
+				LLUUID animation = state->mCurrentAnimationID;
+				if (animation.notNull())
+				{
+					LL_DEBUGS("AOEngine") << "Stopping leftover animation from state " << state->mName << LL_ENDL;
+					gAgent.sendAnimationRequest(animation, ANIM_REQUEST_STOP);
+					gAgentAvatarp->LLCharacter::stopMotion(animation);
+					state->mCurrentAnimationID.setNull();
+				}
+			}
+			else
+			{
+				LL_DEBUGS("AOEngine") << "state "<< index <<" returned NULL." << LL_ENDL;
+			}
+		}
+
+		// restore Linden animation if applicable
+		if (mLastOverriddenMotion != ANIM_AGENT_SIT || !foreignAnimations())
+		{
+			gAgent.sendAnimationRequest(mLastMotion, ANIM_REQUEST_START);
+		}
+
+		mCurrentSet->stopTimer();
+	}
+}
+
+void AOEngine::setStateCycleTimer(const AOSet::AOState* state)
+{
+	F32 timeout = state->mCycleTime;
+	LL_DEBUGS("AOEngine") << "Setting cycle timeout for state " << state->mName << " of " << timeout << LL_ENDL;
+	if (timeout > 0.0f)
+	{
+		mCurrentSet->startTimer(timeout);
+	}
+}
+
+const LLUUID AOEngine::override(const LLUUID& motion, bool start)
+{
+	LL_DEBUGS("AOEngine") << "override(" << gAnimLibrary.animationName(motion) << "," << start << ")" << LL_ENDL;
+
+	if (!mEnabled)
+	{
+		if (start && mCurrentSet)
+		{
+			const AOSet::AOState* state = mCurrentSet->getStateByRemapID(motion);
+			if (state)
+			{
+				setLastMotion(motion);
+				LL_DEBUGS("AOEngine") << "(disabled AO) setting last motion id to " <<  gAnimLibrary.animationName(mLastMotion) << LL_ENDL;
+				if (!state->mAnimations.empty())
+				{
+					setLastOverriddenMotion(motion);
+					LL_DEBUGS("AOEngine") << "(disabled AO) setting last overridden motion id to " <<  gAnimLibrary.animationName(mLastOverriddenMotion) << LL_ENDL;
+				}
+			}
+		}
+		return LLUUID::null;
+	}
+
+	if (mSets.empty())
+	{
+		LL_DEBUGS("AOEngine") << "No sets loaded. Skipping overrider." << LL_ENDL;
+		return LLUUID::null;
+	}
+
+	if (!mCurrentSet)
+	{
+		LL_DEBUGS("AOEngine") << "No current AO set chosen. Skipping overrider." << LL_ENDL;
+		return LLUUID::null;
+	}
+
+	// ignore stopping this motion once so we can stop the Linden animation
+	// without killing our overrider when logging in or re-enabling
+	if (!start && motion == mIgnoreMotionStopOnce)
+	{
+		LL_DEBUGS("AOEngine") << "Not stop-overriding motion " << gAnimLibrary.animationName(motion)
+			<< " within same state." << LL_ENDL;
+
+		mIgnoreMotionStopOnce = LLUUID::null;
+
+		// when stopping a sit motion make sure to stop the cycle point cover-up animation
+		if (motion == ANIM_AGENT_SIT)
+		{
+			gAgent.sendAnimationRequest(ANIM_AGENT_SIT_GENERIC, ANIM_REQUEST_STOP);
+		}
+
+		return LLUUID::null;
+	}
+
+	// map the requested motion to an animation state, taking underwater
+	// swimming into account where applicable
+	AOSet::AOState* state = getStateForMotion(motion);
+	if (!state)
+	{
+		LL_DEBUGS("AOEngine") << "No current AO state for motion " << gAnimLibrary.animationName(motion) << LL_ENDL;
+//		This part of the code was added to capture an edge case where animations got stuck
+//		However, it seems it isn't needed anymore and breaks other, more important cases.
+//		So we disable this code for now, unless bad things happen and the stuck animations
+//		come back again. -Zi
+//		if (!gAnimLibrary.animStateToString(motion) && !start)
+//		{
+//			state = mCurrentSet->getStateByRemapID(mLastOverriddenMotion);
+//			if (state && state->mCurrentAnimationID == motion)
+//			{
+//				LL_DEBUGS("AOEngine") << "Stop requested for current overridden animation UUID " << motion << " - Skipping." << LL_ENDL;
+//			}
+//			else
+//			{
+//				LL_DEBUGS("AOEngine") << "Stop requested for unknown UUID " << motion << " - Stopping it just in case." << LL_ENDL;
+//				gAgent.sendAnimationRequest(motion, ANIM_REQUEST_STOP);
+//				gAgentAvatarp->LLCharacter::stopMotion(motion);
+//			}
+//		}
+		return LLUUID::null;
+	}
+
+	mAnimationChangedSignal(LLUUID::null);
+
+	// clean up stray animations as an additional safety measure
+	// unless current motion is ANIM_AGENT_TYPE, which is a special
+	// case, as it plays at the same time as other motions
+	if (motion != ANIM_AGENT_TYPE)
+	{
+		constexpr S32 cleanupStates[]=
+		{
+			AOSet::Standing,
+			AOSet::Walking,
+			AOSet::Running,
+			AOSet::Sitting,
+			AOSet::SittingOnGround,
+			AOSet::Crouching,
+			AOSet::CrouchWalking,
+			AOSet::Falling,
+			AOSet::FlyingDown,
+			AOSet::FlyingUp,
+			AOSet::Flying,
+			AOSet::FlyingSlow,
+			AOSet::Hovering,
+			AOSet::Jumping,
+			AOSet::TurningRight,
+			AOSet::TurningLeft,
+			AOSet::Floating,
+			AOSet::SwimmingForward,
+			AOSet::SwimmingUp,
+			AOSet::SwimmingDown,
+			AOSet::AOSTATES_MAX		// end marker, guaranteed to be different from any other entry
+		};
+
+		S32 index = 0;
+		S32 stateNum;
+
+		// loop through the list of states
+		while ((stateNum = cleanupStates[index]) != AOSet::AOSTATES_MAX)
+		{
+			// check if the next state is the one we are currently animating and skip that
+			AOSet::AOState* stateToCheck = mCurrentSet->getState(stateNum);
+			if (stateToCheck != state)
+			{
+				// check if there is an animation left over for that state
+				if (!stateToCheck->mCurrentAnimationID.isNull())
+				{
+					LL_WARNS() << "cleaning up animation in state " << stateToCheck->mName << LL_ENDL;
+
+					// stop  the leftover animation locally and in the region for everyone
+					gAgent.sendAnimationRequest(stateToCheck->mCurrentAnimationID, ANIM_REQUEST_STOP);
+					gAgentAvatarp->LLCharacter::stopMotion(stateToCheck->mCurrentAnimationID);
+
+					// mark the state as clean
+					stateToCheck->mCurrentAnimationID.setNull();
+				}
+			}
+			index++;
+		}
+	}
+
+	LLUUID animation;
+
+	mCurrentSet->stopTimer();
+	if (start)
+	{
+		setLastMotion(motion);
+		LL_DEBUGS("AOEngine") << "(enabled AO) setting last motion id to " <<  gAnimLibrary.animationName(mLastMotion) << LL_ENDL;
+
+		// Disable start stands in Mouselook
+		if (mCurrentSet->getMouselookStandDisable() &&
+			motion == ANIM_AGENT_STAND &&
+			mInMouselook)
+		{
+			LL_DEBUGS("AOEngine") << "(enabled AO, mouselook stand stopped) setting last motion id to " <<  gAnimLibrary.animationName(mLastMotion) << LL_ENDL;
+			return LLUUID::null;
+		}
+
+		// Don't override start and turning stands if stand override is disabled
+		if (!mEnabledStands &&
+			(motion == ANIM_AGENT_STAND || motion == ANIM_AGENT_TURNRIGHT || motion == ANIM_AGENT_TURNLEFT))
+		{
+			LL_DEBUGS("AOEngine") << "(enabled AO, stands disabled) setting last motion id to " <<  gAnimLibrary.animationName(mLastMotion) << LL_ENDL;
+			return LLUUID::null;
+		}
+
+		// Do not start override sits if not selected
+		if (!mCurrentSet->getSitOverride() && motion == ANIM_AGENT_SIT)
+		{
+			LL_DEBUGS("AOEngine") << "(enabled AO, sit override stopped) setting last motion id to " <<  gAnimLibrary.animationName(mLastMotion) << LL_ENDL;
+			return LLUUID::null;
+		}
+
+		// scripted seats that use ground_sit as animation need special treatment
+		if (motion == ANIM_AGENT_SIT_GROUND || motion == ANIM_AGENT_SIT_GROUND_CONSTRAINED)
+		{
+			const LLViewerObject* agentRoot = dynamic_cast<LLViewerObject*>(gAgentAvatarp->getRoot());
+			if (agentRoot && agentRoot->getID() != gAgentID)
+			{
+				LL_DEBUGS("AOEngine") << "Ground sit animation playing but sitting on a prim - disabling overrider." << LL_ENDL;
+				return LLUUID::null;
+			}
+		}
+
+		if (!state->mAnimations.empty())
+		{
+			setLastOverriddenMotion(motion);
+			LL_DEBUGS("AOEngine") << "(enabled AO) setting last overridden motion id to " <<  gAnimLibrary.animationName(mLastOverriddenMotion) << LL_ENDL;
+		}
+
+		// do not remember typing as set-wide motion
+		if (motion != ANIM_AGENT_TYPE)
+		{
+			mCurrentSet->setMotion(motion);
+		}
+
+		animation = mCurrentSet->getAnimationForState(state);
+
+		if (state->mCurrentAnimationID.notNull())
+		{
+			LL_DEBUGS("AOEngine")	<< "Previous animation for state "
+						<< gAnimLibrary.animationName(motion)
+						<< " was not stopped, but we were asked to start a new one. Killing old animation." << LL_ENDL;
+			gAgent.sendAnimationRequest(state->mCurrentAnimationID, ANIM_REQUEST_STOP);
+			gAgentAvatarp->LLCharacter::stopMotion(state->mCurrentAnimationID);
+		}
+
+		state->mCurrentAnimationID = animation;
+		LL_DEBUGS("AOEngine")	<< "overriding " <<  gAnimLibrary.animationName(motion)
+					<< " with " << animation
+					<< " in state " << state->mName
+					<< " of set " << mCurrentSet->getName()
+					<< " (" << mCurrentSet << ")" << LL_ENDL;
+
+		if (animation.notNull() && state->mCurrentAnimation < state->mAnimations.size())
+		{
+			mAnimationChangedSignal(state->mAnimations[state->mCurrentAnimation].mInventoryUUID);
+		}
+
+		setStateCycleTimer(state);
+
+		if (motion == ANIM_AGENT_SIT)
+		{
+			// Use ANIM_AGENT_SIT_GENERIC, so we don't create an overrider loop with ANIM_AGENT_SIT
+			// while still having a base sitting pose to cover up cycle points
+			gAgent.sendAnimationRequest(ANIM_AGENT_SIT_GENERIC, ANIM_REQUEST_START);
+			if (mCurrentSet->getSmart())
+			{
+				mSitCancelTimer.oneShot();
+			}
+		}
+		// special treatment for "transient animations" because the viewer needs the Linden animation to know the agent's state
+		else if (motion == ANIM_AGENT_SIT_GROUND ||
+				motion == ANIM_AGENT_SIT_GROUND_CONSTRAINED ||
+				motion == ANIM_AGENT_PRE_JUMP ||
+				motion == ANIM_AGENT_STANDUP ||
+				motion == ANIM_AGENT_LAND ||
+				motion == ANIM_AGENT_MEDIUM_LAND)
+		{
+			gAgent.sendAnimationRequest(animation, ANIM_REQUEST_START);
+			return LLUUID::null;
+		}
+	}
+	else
+	{
+		// check for previously remembered transition motion from/to underwater movement and don't
+		// allow the overrider to stop it, or it will cancel the transition to underwater motion
+		// immediately after starting it
+		if (motion == mTransitionId)
+		{
+			// clear transition motion id and return a null UUID to allow the stock Linden animation
+			// system to take over
+			mTransitionId.setNull();
+			return LLUUID::null;
+		}
+
+		// clear transition motion id here as well, to make sure there is no stray id left behind
+		mTransitionId.setNull();
+
+		animation = state->mCurrentAnimationID;
+		state->mCurrentAnimationID.setNull();
+
+		// for typing animaiton, just return the stored animation, reset the state timer, and don't memorize anything else
+		if (motion == ANIM_AGENT_TYPE)
+		{
+			AOSet::AOState* previousState = mCurrentSet->getStateByRemapID(mLastMotion);
+			if (previousState)
+			{
+				setStateCycleTimer(previousState);
+			}
+			return animation;
+		}
+
+		// 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 (motion == ANIM_AGENT_SIT)
+		{
+			stopAllSitVariants();
+		}
+
+		if (motion != mCurrentSet->getMotion())
+		{
+			LL_WARNS("AOEngine") << "trying to stop-override motion " <<  gAnimLibrary.animationName(motion)
+					<< " but the current set has motion " <<  gAnimLibrary.animationName(mCurrentSet->getMotion()) << LL_ENDL;
+			return animation;
+		}
+
+		mCurrentSet->setMotion(LLUUID::null);
+
+		// again, special treatment for "transient" animations to make sure our own animation gets stopped properly
+		if (motion == ANIM_AGENT_SIT_GROUND ||
+			motion == ANIM_AGENT_SIT_GROUND_CONSTRAINED ||
+			motion == ANIM_AGENT_PRE_JUMP ||
+			motion == ANIM_AGENT_STANDUP ||
+			motion == ANIM_AGENT_LAND ||
+			motion == ANIM_AGENT_MEDIUM_LAND)
+		{
+			gAgent.sendAnimationRequest(animation, ANIM_REQUEST_STOP);
+			gAgentAvatarp->LLCharacter::stopMotion(animation);
+			setStateCycleTimer(state);
+			return LLUUID::null;
+		}
+	}
+
+	return animation;
+}
+
+void AOEngine::checkSitCancel()
+{
+	if (foreignAnimations())
+	{
+		if (AOSet::AOState* aoState = mCurrentSet->getStateByRemapID(ANIM_AGENT_SIT); aoState)
+		{
+			LLUUID animation = aoState->mCurrentAnimationID;
+			if (animation.notNull())
+			{
+				LL_DEBUGS("AOEngine") << "Stopping sit animation due to foreign animations running" << LL_ENDL;
+				gAgent.sendAnimationRequest(animation, ANIM_REQUEST_STOP);
+				// remove cycle point cover-up
+				gAgent.sendAnimationRequest(ANIM_AGENT_SIT_GENERIC, ANIM_REQUEST_STOP);
+				gAgentAvatarp->LLCharacter::stopMotion(animation);
+				mSitCancelTimer.stop();
+				// stop cycle tiemr
+				mCurrentSet->stopTimer();
+			}
+		}
+	}
+}
+
+void AOEngine::cycleTimeout(const AOSet* set)
+{
+	if (!mEnabled)
+	{
+		return;
+	}
+
+	if (set != mCurrentSet)
+	{
+		LL_WARNS("AOEngine") << "cycleTimeout for set " << set->getName() << " but current set is " << mCurrentSet->getName() << LL_ENDL;
+		return;
+	}
+
+	cycle(CycleAny);
+}
+
+void AOEngine::cycle(eCycleMode cycleMode)
+{
+	if (!mEnabled)
+	{
+		return;
+	}
+
+	if (!mCurrentSet)
+	{
+		LL_DEBUGS("AOEngine") << "cycle without set." << LL_ENDL;
+		return;
+	}
+
+	// do not cycle if we're sitting and sit-override is off
+	if (mLastMotion == ANIM_AGENT_SIT && !mCurrentSet->getSitOverride())
+	{
+		return;
+	}
+	// do not cycle if we're standing and mouselook stand override is disabled while being in mouselook
+	else if (mLastMotion == ANIM_AGENT_STAND && mCurrentSet->getMouselookStandDisable() && mInMouselook)
+	{
+		return;
+	}
+
+	AOSet::AOState* state = mCurrentSet->getStateByRemapID(mLastMotion);
+	if (!state)
+	{
+		LL_DEBUGS("AOEngine") << "cycle without state." << LL_ENDL;
+		return;
+	}
+
+	if (!state->mAnimations.size())
+	{
+		LL_DEBUGS("AOEngine") << "cycle without animations in state." << LL_ENDL;
+		return;
+	}
+
+	// make sure we disable cycling only for timed cycle, so manual cycling still works, even with cycling switched off
+	if (!state->mCycle && cycleMode == CycleAny)
+	{
+		LL_DEBUGS("AOEngine") << "cycle timeout, but state is set to not cycling." << LL_ENDL;
+		return;
+	}
+
+	LLUUID oldAnimation = state->mCurrentAnimationID;
+	LLUUID animation;
+
+	if (cycleMode == CycleAny)
+	{
+		animation = mCurrentSet->getAnimationForState(state);
+	}
+	else
+	{
+		if (cycleMode == CyclePrevious)
+		{
+			if (state->mCurrentAnimation == 0)
+			{
+				state->mCurrentAnimation = state->mAnimations.size() - 1;
+			}
+			else
+			{
+				state->mCurrentAnimation--;
+			}
+		}
+		else if (cycleMode == CycleNext)
+		{
+			state->mCurrentAnimation++;
+			if (state->mCurrentAnimation == state->mAnimations.size())
+			{
+				state->mCurrentAnimation = 0;
+			}
+		}
+
+		AOSet::AOAnimation& anim = state->mAnimations[state->mCurrentAnimation];
+
+		if (anim.mAssetUUID.isNull())
+		{
+			LL_DEBUGS("AOEngine") << "Asset UUID for cycled animation " << anim.mName << " not yet known, try to find it." << LL_ENDL;
+
+			if(LLViewerInventoryItem* item = gInventory.getItem(anim.mOriginalUUID) ; item)
+			{
+				LL_DEBUGS("AOEngine") << "Found asset UUID for cycled animation: " << item->getAssetUUID() << " - Updating AOAnimation.mAssetUUID" << LL_ENDL;
+				anim.mAssetUUID = item->getAssetUUID();
+			}
+			else
+			{
+				LL_DEBUGS("AOEngine") << "Inventory UUID " << anim.mOriginalUUID << " for cycled animation " << anim.mName << " still returns no asset." << LL_ENDL;
+			}
+		}
+
+		animation = anim.mAssetUUID;
+	}
+
+	// don't do anything if the animation didn't change
+	if (animation == oldAnimation)
+	{
+		return;
+	}
+
+	mAnimationChangedSignal(LLUUID::null);
+
+	state->mCurrentAnimationID = animation;
+	if (animation.notNull())
+	{
+		LL_DEBUGS("AOEngine") << "requesting animation start for motion " << gAnimLibrary.animationName(mLastMotion) << ": " << animation << LL_ENDL;
+		gAgent.sendAnimationRequest(animation, ANIM_REQUEST_START);
+		mAnimationChangedSignal(state->mAnimations[state->mCurrentAnimation].mInventoryUUID);
+	}
+	else
+	{
+		LL_DEBUGS("AOEngine") << "overrider came back with NULL animation for motion " << gAnimLibrary.animationName(mLastMotion) << "." << LL_ENDL;
+	}
+
+	if (oldAnimation.notNull())
+	{
+		LL_DEBUGS("AOEngine") << "Cycling state " << state->mName << " - stopping animation " << oldAnimation << LL_ENDL;
+		gAgent.sendAnimationRequest(oldAnimation, ANIM_REQUEST_STOP);
+		gAgentAvatarp->LLCharacter::stopMotion(oldAnimation);
+	}
+}
+
+void AOEngine::updateSortOrder(AOSet::AOState* state)
+{
+	for (U32 index = 0; index < state->mAnimations.size(); ++index)
+	{
+		U32 sortOrder = state->mAnimations[index].mSortOrder;
+
+		if (sortOrder != index)
+		{
+			std::ostringstream numStr("");
+			numStr << index;
+
+			LL_DEBUGS("AOEngine")	<< "sort order is " << sortOrder << " but index is " << index
+						<< ", setting sort order description: " << numStr.str() << LL_ENDL;
+
+			state->mAnimations[index].mSortOrder = index;
+
+			LLViewerInventoryItem* item = gInventory.getItem(state->mAnimations[index].mInventoryUUID);
+			if (!item)
+			{
+				LL_WARNS("AOEngine") << "NULL inventory item found while trying to copy " << state->mAnimations[index].mInventoryUUID << LL_ENDL;
+				continue;
+			}
+			LLPointer<LLViewerInventoryItem> newItem = new LLViewerInventoryItem(item);
+
+			newItem->setDescription(numStr.str());
+			newItem->setComplete(TRUE);
+			newItem->updateServer(FALSE);
+
+			gInventory.updateItem(newItem);
+		}
+	}
+}
+
+void AOEngine::addSet(const std::string& name, inventory_func_type callback, bool reload)
+{
+	if (mAOFolder.isNull())
+	{
+		LL_WARNS("AOEngine") << ROOT_AO_FOLDER << " folder not there yet. Requesting recreation." << LL_ENDL;
+		tick();
+		return;
+	}
+
+	BOOL wasProtected = gSavedPerAccountSettings.getBOOL("LockAOFolders");
+	gSavedPerAccountSettings.setBOOL("LockAOFolders", FALSE);
+	LL_DEBUGS("AOEngine") << "adding set folder " << name << LL_ENDL;
+	gInventory.createNewCategory(mAOFolder, LLFolderType::FT_NONE, name, [callback, wasProtected](const LLUUID &new_cat_id)
+	{
+		gSavedPerAccountSettings.setBOOL("LockAOFolders", wasProtected);
+
+		callback(new_cat_id);
+	});
+
+	if (reload)
+	{
+		mTimerCollection.enableReloadTimer(true);
+	}
+}
+
+bool AOEngine::createAnimationLink(AOSet::AOState* state, const LLInventoryItem* item)
+{
+	LL_DEBUGS("AOEngine") << "Asset ID " << item->getAssetUUID() << " inventory id " << item->getUUID() << " category id " << state->mInventoryUUID << LL_ENDL;
+	LL_DEBUGS("AOEngine") << "state " << state->mName << " item " << item->getName() << LL_ENDL;
+
+	if (state->mInventoryUUID.isNull())
+	{
+		LL_DEBUGS("AOEngine") << "state inventory UUID not found, failing." << LL_ENDL;
+		return false;
+	}
+
+	LLInventoryObject::const_object_list_t obj_array{ LLConstPointer<LLInventoryObject>(item) };
+	link_inventory_array(state->mInventoryUUID,
+							obj_array,
+							LLPointer<LLInventoryCallback>(nullptr));
+
+	return true;
+}
+
+void 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.mOriginalUUID = item->getLinkedUUID();
+	anim.mName = item->getName();
+	anim.mSortOrder = state->mAnimations.size() + 1;
+	state->mAnimations.emplace_back(std::move(anim));
+
+	BOOL wasProtected = gSavedPerAccountSettings.getBOOL("LockAOFolders");
+	gSavedPerAccountSettings.setBOOL("LockAOFolders", FALSE);
+	bool success = createAnimationLink(state, item);
+	gSavedPerAccountSettings.setBOOL("LockAOFolders", wasProtected);
+
+	if(success)
+	{
+		if (reload)
+		{
+			mTimerCollection.enableReloadTimer(true);
+		}
+		return;
+	}
+
+	// creating the animation link failed, so we need to create a new folder for this state -
+	// add the animation asset to the queue of animations to insert into the state - this takes
+	// care of multi animation drag & drop that come in faster than the viewer can create a new
+	// inventory folder
+	state->mAddQueue.push_back(item);
+
+	// if this is the first queued animation for this state, create the folder asyncronously
+	if(state->mAddQueue.size() == 1)
+	{
+		gInventory.createNewCategory(set->getInventoryUUID(), LLFolderType::FT_NONE, state->mName, [this, state, reload, wasProtected](const LLUUID &new_cat_id)
+		{
+			state->mInventoryUUID = new_cat_id;
+			gSavedPerAccountSettings.setBOOL("LockAOFolders", FALSE);
+
+			// add all queued animations to this state's folder and then clear the queue
+			for (const auto item : state->mAddQueue)
+			{
+				createAnimationLink(state, item);
+			}
+			state->mAddQueue.clear();
+
+			gSavedPerAccountSettings.setBOOL("LockAOFolders", wasProtected);
+
+			if (reload)
+			{
+				mTimerCollection.enableReloadTimer(true);
+			}
+		});
+	}
+}
+
+bool AOEngine::findForeignItems(const LLUUID& uuid) const
+{
+	bool moved = false;
+
+	LLInventoryModel::item_array_t* items;
+	LLInventoryModel::cat_array_t* cats;
+
+	gInventory.getDirectDescendentsOf(uuid, cats, items);
+
+	if (cats)
+	{
+		for (const auto& cat : *cats)
+		{
+			if (findForeignItems(cat->getUUID()))
+			{
+				moved = true;
+			}
+		}
+	}
+
+	// count backwards in case we have to remove items
+	BOOL wasProtected = gSavedPerAccountSettings.getBOOL("LockAOFolders");
+	gSavedPerAccountSettings.setBOOL("LockAOFolders", FALSE);
+
+	if (items)
+	{
+		for (S32 index = items->size() - 1; index >= 0; --index)
+		{
+			bool move = false;
+
+			LLPointer<LLViewerInventoryItem> item = items->at(index);
+			if (item->getIsLinkType())
+			{
+				if (item->getInventoryType() != LLInventoryType::IT_ANIMATION)
+				{
+					LL_DEBUGS("AOEngine") << item->getName() << " is a link but does not point to an animation." << LL_ENDL;
+					move = true;
+				}
+				else
+				{
+					LL_DEBUGS("AOEngine") << item->getName() << " is an animation link." << LL_ENDL;
+				}
+			}
+			else
+			{
+				LL_DEBUGS("AOEngine") << item->getName() << " is not a link!" << LL_ENDL;
+				move = true;
+			}
+
+			if (move)
+			{
+				moved = true;
+				gInventory.changeItemParent(item, gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND), false);
+				LL_DEBUGS("AOEngine") << item->getName() << " moved to lost and found!" << LL_ENDL;
+			}
+		}
+	}
+
+	gSavedPerAccountSettings.setBOOL("LockAOFolders", wasProtected);
+
+	return moved;
+}
+
+// needs a three-step process, since purge of categories only seems to work from trash
+void AOEngine::purgeFolder(const LLUUID& uuid) const
+{
+	// unprotect it
+	BOOL wasProtected = gSavedPerAccountSettings.getBOOL("LockAOFolders");
+	gSavedPerAccountSettings.setBOOL("LockAOFolders", FALSE);
+
+	// move everything that's not an animation link to "lost and found"
+	if (findForeignItems(uuid))
+	{
+		LLNotificationsUtil::add("AOForeignItemsFound", LLSD());
+	}
+
+	// trash it
+	gInventory.removeCategory(uuid);
+
+	// clean it
+	purge_descendents_of(uuid, nullptr);
+	gInventory.notifyObservers();
+
+	// purge it
+	remove_inventory_object(uuid, nullptr);
+	gInventory.notifyObservers();
+
+	// protect it
+	gSavedPerAccountSettings.setBOOL("LockAOFolders", wasProtected);
+}
+
+bool AOEngine::removeSet(AOSet* set)
+{
+	purgeFolder(set->getInventoryUUID());
+
+	mTimerCollection.enableReloadTimer(true);
+	return true;
+}
+
+bool AOEngine::removeAnimation(const AOSet* set, AOSet::AOState* state, S32 index)
+{
+	if (index < 0)
+	{
+		return false;
+	}
+
+	auto numOfAnimations = state->mAnimations.size();
+	if (numOfAnimations == 0)
+	{
+		return false;
+	}
+
+	LLViewerInventoryItem* item = gInventory.getItem(state->mAnimations[index].mInventoryUUID);
+
+	// check if this item is actually an animation link
+	bool move = true;
+	if (item->getIsLinkType())
+	{
+		if (item->getInventoryType() == LLInventoryType::IT_ANIMATION)
+		{
+			// it is an animation link, so mark it to be purged
+			move = false;
+		}
+	}
+
+	// this item was not an animation link, move it to lost and found
+	if (move)
+	{
+		gInventory.changeItemParent(item, gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND), false);
+		LLNotificationsUtil::add("AOForeignItemsFound", LLSD());
+		update();
+		return false;
+	}
+
+	// purge the item from inventory
+	LL_DEBUGS("AOEngine") << __LINE__ << " purging: " << state->mAnimations[index].mInventoryUUID << LL_ENDL;
+	remove_inventory_object(state->mAnimations[index].mInventoryUUID, nullptr); // item->getUUID());
+	gInventory.notifyObservers();
+
+	state->mAnimations.erase(state->mAnimations.begin() + index);
+
+	if (state->mAnimations.empty())
+	{
+		LL_DEBUGS("AOEngine") << "purging folder " << state->mName << " from inventory because it's empty." << LL_ENDL;
+
+		LLInventoryModel::item_array_t* items;
+		LLInventoryModel::cat_array_t* cats;
+		gInventory.getDirectDescendentsOf(set->getInventoryUUID(), cats, items);
+
+		if (cats)
+		{
+			for (const auto& cat : *cats)
+			{
+				std::vector<std::string> params;
+				LLStringUtil::getTokens(cat->getName(), params, ":");
+
+				if (params.empty())
+				{
+					LL_WARNS("AOEngine") << "Unexpected folder found in ao set folder: " << cat->getName() << LL_ENDL;
+					return false;
+				}
+
+				const std::string& stateName = params[0];
+
+				if (state->mName.compare(stateName) == 0)
+				{
+					LL_DEBUGS("AOEngine") << "folder found: " << cat->getName() << " purging uuid " << cat->getUUID() << LL_ENDL;
+
+					purgeFolder(cat->getUUID());
+					state->mInventoryUUID.setNull();
+					break;
+				}
+			}
+		}
+	}
+	else
+	{
+		updateSortOrder(state);
+	}
+
+	return true;
+}
+
+bool AOEngine::swapWithPrevious(AOSet::AOState* state, S32 index)
+{
+	auto 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)
+{
+	auto 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);
+
+	if (items)
+	{
+		for (const auto& item : *items)
+		{
+			LL_DEBUGS("AOEngine") << "Found animation link " << item->LLInventoryItem::getName()
+				<< " desc " << item->LLInventoryItem::getDescription()
+				<< " asset " << item->getAssetUUID() << LL_ENDL;
+
+			AOSet::AOAnimation anim;
+			anim.mName = item->LLInventoryItem::getName();
+			anim.mInventoryUUID = item->getUUID();
+			anim.mOriginalUUID = item->getLinkedUUID();
+
+			anim.mAssetUUID = LLUUID::null;
+
+			// if we can find the original animation already right here, save its asset ID, otherwise this will
+			// be tried again in AOSet::getAnimationForState() and/or AOEngine::cycle()
+			if (item->getLinkedItem())
+			{
+				anim.mAssetUUID = item->getAssetUUID();
+			}
+
+			S32 sortOrder;
+			if (!LLStringUtil::convertToS32(item->LLInventoryItem::getDescription(), sortOrder))
+			{
+				sortOrder = -1;
+			}
+			anim.mSortOrder = sortOrder;
+
+			LL_DEBUGS("AOEngine") << "current sort order is " << sortOrder << LL_ENDL;
+
+			if (sortOrder == -1)
+			{
+				LL_WARNS("AOEngine") << "sort order was unknown so append to the end of the list" << LL_ENDL;
+				state->mAnimations.emplace_back(std::move(anim));
+			}
+			else
+			{
+				bool inserted = false;
+				for (auto index = 0; index < state->mAnimations.size(); ++index)
+				{
+					if (state->mAnimations[index].mSortOrder > sortOrder)
+					{
+						LL_DEBUGS("AOEngine") << "inserting at index " << index << LL_ENDL;
+						state->mAnimations.insert(state->mAnimations.begin() + index, anim);
+						inserted = true;
+						break;
+					}
+				}
+				if (!inserted)
+				{
+					LL_DEBUGS("AOEngine") << "not inserted yet, appending to the list instead" << LL_ENDL;
+					state->mAnimations.emplace_back(std::move(anim));
+				}
+			}
+			LL_DEBUGS("AOEngine") << "Animation count now: " << state->mAnimations.size() << LL_ENDL;
+		}
+	}
+
+	updateSortOrder(state);
+}
+
+void AOEngine::update()
+{
+	if (mAOFolder.isNull())
+	{
+		return;
+	}
+
+	if (!gInventory.isCategoryComplete(mAOFolder))
+	{
+		LL_DEBUGS("AOEngine") << "#AO folder hasn't fully fetched yet, try again next timer tick." << LL_ENDL;
+		gInventory.fetchDescendentsOf(mAOFolder);
+		return;
+	}
+
+	// move everything that's not an animation link to "lost and found"
+	if (findForeignItems(mAOFolder))
+	{
+		LLNotificationsUtil::add("AOForeignItemsFound", LLSD());
+	}
+
+	LLInventoryModel::cat_array_t* categories;
+	LLInventoryModel::item_array_t* items;
+
+	bool allComplete = true;
+	mTimerCollection.enableSettingsTimer(false);
+
+	gInventory.getDirectDescendentsOf(mAOFolder, categories, items);
+
+	if (categories)
+	{
+		for (const auto& currentCategory : *categories)
+		{
+			const std::string& setFolderName = currentCategory->getName();
+
+			if (setFolderName.empty())
+			{
+				LL_WARNS("AOEngine") << "Folder with emtpy name in AO folder" << LL_ENDL;
+				continue;
+			}
+
+			std::vector<std::string> params;
+			LLStringUtil::getTokens(setFolderName, params, ":");
+			if (params.empty())
+			{
+				LL_WARNS("AOEngine") << "Unexpected folder found in ao set folder: " << currentCategory->getName() << LL_ENDL;
+				continue;
+			}
+
+			AOSet* newSet = getSetByName(params[0]);
+			if (!newSet)
+			{
+				LL_DEBUGS("AOEngine") << "Adding set " << setFolderName << " to AO." << LL_ENDL;
+				newSet = new AOSet(currentCategory->getUUID());
+				newSet->setName(params[0]);
+				mSets.emplace_back(newSet);
+			}
+			else
+			{
+				if (newSet->getComplete())
+				{
+					LL_DEBUGS("AOEngine") << "Set " << params[0] << " already complete. Skipping." << LL_ENDL;
+					continue;
+				}
+				LL_DEBUGS("AOEngine") << "Updating set " << setFolderName << " in AO." << LL_ENDL;
+			}
+			allComplete = false;
+
+			for (auto num = 1; num < params.size(); ++num)
+			{
+				if (params[num].size() != 2)
+				{
+					LL_WARNS("AOEngine") << "Unknown AO set option " << params[num] << LL_ENDL;
+				}
+				else if (params[num] == "SO")
+				{
+					newSet->setSitOverride(true);
+				}
+				else if (params[num] == "SM")
+				{
+					newSet->setSmart(true);
+				}
+				else if (params[num] == "DM")
+				{
+					newSet->setMouselookStandDisable(true);
+				}
+				else if (params[num] == "**")
+				{
+					mDefaultSet = newSet;
+					mCurrentSet = newSet;
+					mSetChangedSignal(mCurrentSet->getName());
+				}
+				else
+				{
+					LL_WARNS("AOEngine") << "Unknown AO set option " << params[num] << LL_ENDL;
+				}
+			}
+
+			if (gInventory.isCategoryComplete(currentCategory->getUUID()))
+			{
+				LL_DEBUGS("AOEngine") << "Set " << params[0] << " is complete, reading states ..." << LL_ENDL;
+
+				LLInventoryModel::cat_array_t* stateCategories;
+				gInventory.getDirectDescendentsOf(currentCategory->getUUID(), stateCategories, items);
+				newSet->setComplete(true);
+
+				for (const auto& stateCategory : *stateCategories)
+				{
+					std::vector<std::string> state_params;
+					LLStringUtil::getTokens(stateCategory->getName(), state_params, ":");
+					if (params.empty())
+					{
+						LL_WARNS("AOEngine") << "Unexpected state folder found in ao set: " << stateCategory->getName() << LL_ENDL;
+						continue;
+					}
+					const std::string& stateName = state_params[0];
+
+					AOSet::AOState* state = newSet->getStateByName(stateName);
+					if (!state)
+					{
+						LL_WARNS("AOEngine") << "Unknown state " << stateName << ". Skipping." << LL_ENDL;
+						continue;
+					}
+					LL_DEBUGS("AOEngine") << "Reading state " << stateName << LL_ENDL;
+
+					state->mInventoryUUID = stateCategory->getUUID();
+					for (auto num = 1; num < state_params.size(); ++num)
+					{
+						if (state_params[num] == "CY")
+						{
+							state->mCycle = true;
+							LL_DEBUGS("AOEngine") << "Cycle on" << LL_ENDL;
+						}
+						else if (state_params[num] == "RN")
+						{
+							state->mRandom = true;
+							LL_DEBUGS("AOEngine") << "Random on" << LL_ENDL;
+						}
+						else if (state_params[num].substr(0, 2) == "CT")
+						{
+							LLStringUtil::convertToS32(state_params[num].substr(2, state_params[num].size() - 2), state->mCycleTime);
+							LL_DEBUGS("AOEngine") << "Cycle Time specified:" << state->mCycleTime << LL_ENDL;
+						}
+						else
+						{
+							LL_WARNS("AOEngine") << "Unknown AO set option " << state_params[num] << LL_ENDL;
+						}
+					}
+
+					if (!gInventory.isCategoryComplete(state->mInventoryUUID))
+					{
+						LL_DEBUGS("AOEngine") << "State category " << stateName << " is incomplete, fetching descendents" << LL_ENDL;
+						gInventory.fetchDescendentsOf(state->mInventoryUUID);
+						allComplete = false;
+						newSet->setComplete(false);
+						continue;
+					}
+					reloadStateAnimations(state);
+				}
+			}
+			else
+			{
+				LL_DEBUGS("AOEngine") << "Set " << params[0] << " is incomplete, fetching descendents" << LL_ENDL;
+				gInventory.fetchDescendentsOf(currentCategory->getUUID());
+			}
+		}
+	}
+
+	if (allComplete)
+	{
+		mEnabled = gSavedPerAccountSettings.getBOOL("AlchemyAOEnable");
+
+		if (!mCurrentSet && !mSets.empty())
+		{
+			LL_DEBUGS("AOEngine") << "No default set defined, choosing the first in the list." << LL_ENDL;
+			selectSet(mSets[0]);
+		}
+
+		mTimerCollection.enableInventoryTimer(false);
+		mTimerCollection.enableSettingsTimer(true);
+
+		LL_INFOS("AOEngine") << "sending update signal" << LL_ENDL;
+		mUpdatedSignal();
+		enable(mEnabled);
+	}
+}
+
+void AOEngine::reload(bool aFromTimer)
+{
+	bool wasEnabled = mEnabled;
+
+	mTimerCollection.enableReloadTimer(false);
+
+	if (wasEnabled)
+	{
+		enable(false);
+	}
+
+	gAgent.stopCurrentAnimations();
+	mLastOverriddenMotion = ANIM_AGENT_STAND;
+
+	clear(aFromTimer);
+	mAOFolder.setNull();
+	mTimerCollection.enableInventoryTimer(true);
+	tick();
+
+	if (wasEnabled)
+	{
+		enable(true);
+	}
+}
+
+AOSet* AOEngine::getSetByName(const std::string& name) const
+{
+	AOSet* found = nullptr;
+	for (auto set : mSets)
+	{
+		if (set->getName().compare(name) == 0)
+		{
+			found = set;
+			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)
+{
+	if (mEnabled && mCurrentSet)
+	{
+		AOSet::AOState* state = mCurrentSet->getStateByRemapID(mLastOverriddenMotion);
+		if (state)
+		{
+			gAgent.sendAnimationRequest(state->mCurrentAnimationID, ANIM_REQUEST_STOP);
+			state->mCurrentAnimationID.setNull();
+			mCurrentSet->stopTimer();
+		}
+	}
+
+	mCurrentSet = set;
+	mSetChangedSignal(mCurrentSet->getName());
+
+	if (mEnabled)
+	{
+		LL_DEBUGS("AOEngine") << "enabling with motion " << gAnimLibrary.animationName(mLastMotion) << LL_ENDL;
+		gAgent.sendAnimationRequest(override(mLastMotion, true), ANIM_REQUEST_START);
+	}
+}
+
+AOSet* AOEngine::selectSetByName(const std::string& name)
+{
+	AOSet* set = getSetByName(name);
+	if (set)
+	{
+		selectSet(set);
+		return set;
+	}
+	LL_WARNS("AOEngine") << "Could not find AO set " << name << LL_ENDL;
+	return nullptr;
+}
+
+const std::vector<AOSet*> 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->getSmart())
+	{
+		setParams += ":SM";
+	}
+	if (set->getMouselookStandDisable())
+	{
+		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());
+	LL_WARNS("AOEngine") << cat << LL_ENDL;
+	cat->rename(setParams);
+	cat->updateServer(FALSE);
+	gInventory.addChangedMask(LLInventoryObserver::LABEL, cat->getUUID());
+	gInventory.notifyObservers();
+*/
+	BOOL wasProtected = gSavedPerAccountSettings.getBOOL("LockAOFolders");
+	gSavedPerAccountSettings.setBOOL("LockAOFolders", FALSE);
+	rename_category(&gInventory, set->getInventoryUUID(), setParams);
+	gSavedPerAccountSettings.setBOOL("LockAOFolders", wasProtected);
+
+	LL_INFOS("AOEngine") << "sending update signal" << LL_ENDL;
+	mUpdatedSignal();
+}
+
+bool AOEngine::renameSet(AOSet* set, const std::string& name)
+{
+	if (name.empty() || name.find(":") != std::string::npos)
+	{
+		return false;
+	}
+	set->setName(name);
+	set->setDirty(true);
+
+	return true;
+}
+
+void AOEngine::saveState(const AOSet::AOState* state)
+{
+	std::string stateParams = state->mName;
+	F32 time = state->mCycleTime;
+	if (time > 0.0f)
+	{
+		std::ostringstream timeStr;
+		timeStr << ":CT" << state->mCycleTime;
+		stateParams += timeStr.str();
+	}
+	if (state->mCycle)
+	{
+		stateParams += ":CY";
+	}
+	if (state->mRandom)
+	{
+		stateParams += ":RN";
+	}
+
+	BOOL wasProtected = gSavedPerAccountSettings.getBOOL("LockAOFolders");
+	gSavedPerAccountSettings.setBOOL("LockAOFolders", FALSE);
+	rename_category(&gInventory, state->mInventoryUUID, stateParams);
+	gSavedPerAccountSettings.setBOOL("LockAOFolders", wasProtected);
+}
+
+void AOEngine::saveSettings()
+{
+	for (auto set : mSets)
+	{
+		if (set->getDirty())
+		{
+			saveSet(set);
+			LL_INFOS("AOEngine") << "dirty set saved " << set->getName() << LL_ENDL;
+			set->setDirty(false);
+		}
+
+		for (S32 stateIndex = 0; stateIndex < AOSet::AOSTATES_MAX; ++stateIndex)
+		{
+			AOSet::AOState* state = set->getState(stateIndex);
+			if (state->mDirty)
+			{
+				saveState(state);
+				LL_INFOS("AOEngine") << "dirty state saved " << state->mName << LL_ENDL;
+				state->mDirty = false;
+			}
+		}
+	}
+}
+
+void AOEngine::inMouselook(bool mouselook)
+{
+	if (mInMouselook == mouselook)
+	{
+		return;
+	}
+
+	mInMouselook = mouselook;
+
+	if (!mCurrentSet)
+	{
+		return;
+	}
+
+	if (!mCurrentSet->getMouselookStandDisable())
+	{
+		return;
+	}
+
+	if (!mEnabled)
+	{
+		return;
+	}
+
+	if (mLastMotion != ANIM_AGENT_STAND)
+	{
+		return;
+	}
+
+	if (mouselook)
+	{
+		AOSet::AOState* state = mCurrentSet->getState(AOSet::Standing);
+		if (!state)
+		{
+			return;
+		}
+
+		LLUUID animation = state->mCurrentAnimationID;
+		if (animation.notNull())
+		{
+			gAgent.sendAnimationRequest(animation, ANIM_REQUEST_STOP);
+			gAgentAvatarp->LLCharacter::stopMotion(animation);
+			state->mCurrentAnimationID.setNull();
+			LL_DEBUGS("AOEngine") << " stopped animation " << animation << " in state " << state->mName << LL_ENDL;
+		}
+		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 (auto set : mSets)
+	{
+		set->setDirty(true);
+	}
+}
+
+void AOEngine::setOverrideSits(AOSet* set, bool override_sit)
+{
+	set->setSitOverride(override_sit);
+	set->setDirty(true);
+
+	if (mCurrentSet != set)
+	{
+		return;
+	}
+
+	if (mLastMotion != ANIM_AGENT_SIT)
+	{
+		return;
+	}
+
+	if (!mEnabled)
+	{
+		return;
+	}
+
+	if (override_sit)
+	{
+		stopAllSitVariants();
+		gAgent.sendAnimationRequest(ANIM_AGENT_SIT_GENERIC, ANIM_REQUEST_START);
+	}
+	else
+	{
+		// remove sit cycle cover up
+		gAgent.sendAnimationRequest(ANIM_AGENT_SIT_GENERIC, ANIM_REQUEST_STOP);
+
+		AOSet::AOState* state = mCurrentSet->getState(AOSet::Sitting);
+		if (state)
+		{
+			LLUUID animation = state->mCurrentAnimationID;
+			if (animation.notNull())
+			{
+				gAgent.sendAnimationRequest(animation, ANIM_REQUEST_STOP);
+				state->mCurrentAnimationID.setNull();
+			}
+		}
+
+		if (!foreignAnimations())
+		{
+			gAgent.sendAnimationRequest(ANIM_AGENT_SIT, ANIM_REQUEST_START);
+		}
+	}
+}
+
+void AOEngine::setSmart(AOSet* set, bool smart)
+{
+	set->setSmart(smart);
+	set->setDirty(true);
+
+	if (!mEnabled)
+	{
+		return;
+	}
+
+	if (smart)
+	{
+		// make sure to restart the sit cancel timer to fix sit overrides when the object we are
+		// sitting on is playing its own animation
+		const LLViewerObject* agentRoot = dynamic_cast<LLViewerObject*>(gAgentAvatarp->getRoot());
+		if (agentRoot && agentRoot->getID() != gAgentID)
+		{
+			mSitCancelTimer.oneShot();
+		}
+	}
+}
+
+void AOEngine::setDisableMouselookStands(AOSet* set, bool disabled)
+{
+	set->setMouselookStandDisable(disabled);
+	set->setDirty(true);
+
+	if (mCurrentSet != set)
+	{
+		return;
+	}
+
+	if (!mEnabled)
+	{
+		return;
+	}
+
+	// make sure an update happens if needed
+	mInMouselook = !gAgentCamera.cameraMouselook();
+	inMouselook(!mInMouselook);
+}
+
+void AOEngine::setCycle(AOSet::AOState* state, bool cycle)
+{
+	state->mCycle = cycle;
+	state->mDirty = true;
+}
+
+void AOEngine::setRandomize(AOSet::AOState* state, bool randomize)
+{
+	state->mRandom = randomize;
+	state->mDirty = true;
+}
+
+void AOEngine::setCycleTime(AOSet::AOState* state, F32 time)
+{
+	state->mCycleTime = time;
+	state->mDirty = true;
+}
+
+void AOEngine::tick()
+{
+	// <FS:ND> make sure agent is alive and kicking before doing anything
+	if (!isAgentAvatarValid())
+	{
+		return;
+	}
+	// </FS:ND>
+
+	const LLUUID categoryID = gInventory.findCategoryUUIDForNameInRoot(ROOT_AO_FOLDER, gInventory.getRootFolderID());
+
+	if (categoryID.isNull())
+	{
+		LL_WARNS("AOEngine") << "no " << ROOT_AO_FOLDER << " folder yet. Creating ..." << LL_ENDL;
+		gInventory.createNewCategory(gInventory.getRootFolderID(), LLFolderType::FT_NONE, ROOT_AO_FOLDER);
+		mAOFolder.setNull();
+	}
+	else
+	{
+		mAOFolder = categoryID;
+		update();
+	}
+}
+
+bool AOEngine::importNotecard(const LLInventoryItem* item)
+{
+	if (item)
+	{
+		LL_INFOS("AOEngine") << "importing AO notecard: " << item->getName() << LL_ENDL;
+		if (getSetByName(item->getName()))
+		{
+			LLNotificationsUtil::add("AOImportSetAlreadyExists", LLSD());
+			return false;
+		}
+
+		if (!gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE) && !gAgent.isGodlike())
+		{
+			LLNotificationsUtil::add("AOImportPermissionDenied", LLSD());
+			return false;
+		}
+
+		if (item->getAssetUUID().notNull())
+		{
+			// create the new set with the folder UUID where the notecard is in, so we can reference it
+			// in the notecard reader, this will later be cleared to make room for the real #AO subfolder
+			mImportSet = new AOSet(item->getParentUUID());
+			mImportSet->setName(item->getName());
+
+			LLUUID* newUUID = new LLUUID(item->getAssetUUID());
+			const LLHost sourceSim;
+
+			gAssetStorage->getInvItemAsset(
+				sourceSim,
+				gAgentID,
+				gAgentSessionID,
+				item->getPermissions().getOwner(),
+				LLUUID::null,
+				item->getUUID(),
+				item->getAssetUUID(),
+				item->getType(),
+				&onNotecardLoadComplete,
+				(void*) newUUID,
+				TRUE
+			);
+
+			return true;
+		}
+	}
+	return false;
+}
+
+// static
+void AOEngine::onNotecardLoadComplete(const LLUUID& assetUUID, LLAssetType::EType type,
+											void* userdata, S32 status, LLExtStat extStatus)
+{
+	if (status != LL_ERR_NOERR)
+	{
+		// AOImportDownloadFailed
+		LLNotificationsUtil::add("AOImportDownloadFailed", LLSD());
+		// NULL tells the importer to cancel all operations and free the import set memory
+		AOEngine::instance().parseNotecard(nullptr);
+		return;
+	}
+	LL_DEBUGS("AOEngine") << "Downloading import notecard complete." << LL_ENDL;
+
+	LLFileSystem file(assetUUID, type, LLFileSystem::READ);
+
+	S32 notecardSize = file.getSize();
+	char* buffer = new char[notecardSize + 1];
+	buffer[notecardSize] = 0;
+
+	BOOL ret = file.read((U8*)buffer, notecardSize);
+	if (ret)
+	{
+		AOEngine::instance().parseNotecard(buffer);
+	}
+	else
+	{
+		delete[] buffer;
+	}
+}
+
+void AOEngine::parseNotecard(const char* buffer)
+{
+	LL_DEBUGS("AOEngine") << "parsing import notecard" << LL_ENDL;
+
+	bool isValid = false;
+
+	if (!buffer)
+	{
+		LL_WARNS("AOEngine") << "buffer==NULL - aborting import" << LL_ENDL;
+		// NOTE: cleanup is always the same, needs streamlining
+		delete mImportSet;
+		mImportSet = nullptr;
+		mUpdatedSignal();
+		return;
+	}
+
+	std::string text(buffer);
+	delete[] buffer;
+
+	std::vector<std::string> lines;
+	LLStringUtil::getTokens(text, lines, "\n");
+
+	auto it = std::find_if(lines.begin(), lines.end(), [](const std::string& line) {
+		return line.find("Text length ") == 0;
+	});
+
+	if (it == lines.end()) {
+		// Line not found
+		LLNotificationsUtil::add("AOImportNoText", LLSD());
+		delete mImportSet;
+		mImportSet = nullptr;
+		mUpdatedSignal();
+		return;
+	}
+
+	// Line found, 'it' points to the found element
+	std::size_t found = std::distance(lines.begin(), it) + 1;
+
+	// mImportSet->getInventoryUUID() right now contains the folder UUID where the notecard is in
+	LLViewerInventoryCategory* importCategory = gInventory.getCategory(mImportSet->getInventoryUUID());
+	if (!importCategory)
+	{
+		LLNotificationsUtil::add("AOImportNoFolder", LLSD());
+		delete mImportSet;
+		mImportSet = nullptr;
+		mUpdatedSignal();
+		return;
+	}
+
+	std::map<std::string, LLUUID> animationMap;
+	LLInventoryModel::cat_array_t* dummy;
+	LLInventoryModel::item_array_t* items;
+
+    gInventory.getDirectDescendentsOf(mImportSet->getInventoryUUID(), dummy, items);
+    for (auto& item : *items)
+    {
+        animationMap[item->getName()] = item->getUUID();
+        LL_DEBUGS("AOEngine") << "animation " << item->getName() << " has inventory UUID " << animationMap[item->getName()] << LL_ENDL;
+    }
+
+	// [ State ]Anim1|Anim2|Anim3
+	for (auto index = found; index < lines.size(); ++index)
+	{
+		std::string line = lines[index];
+
+		// cut off the trailing } of a notecard asset's text portion in the last line
+		if (index == lines.size() - 1)
+		{
+			line = line.substr(0, line.size() - 1);
+		}
+
+		LLStringUtil::trim(line);
+
+		if (line.empty())
+		{
+			continue;
+		}
+
+		if (line[0] == '#') // <ND/> FIRE-3801; skip comments to reduce spam to local chat.
+		{
+			continue;
+		}
+
+		if (line.find("[") != 0)
+		{
+			LLSD args;
+			args["LINE"] = (S32)index;
+			LLNotificationsUtil::add("AOImportNoStatePrefix", args);
+			continue;
+		}
+
+		if (line.find("]") == std::string::npos)
+		{
+			LLSD args;
+			args["LINE"] = (S32)index;
+			LLNotificationsUtil::add("AOImportNoValidDelimiter", args);
+			continue;
+		}
+		auto endTag = line.find("]");
+
+		std::string stateName = line.substr(1, endTag - 1);
+		LLStringUtil::trim(stateName);
+
+		AOSet::AOState* newState = mImportSet->getStateByName(stateName);
+		if (!newState)
+		{
+			LLSD args;
+			args["NAME"] = stateName;
+			LLNotificationsUtil::add("AOImportStateNameNotFound", args);
+			continue;
+		}
+
+		std::string animationLine = line.substr(endTag + 1);
+		std::vector<std::string> animationList;
+		LLStringUtil::getTokens(animationLine, animationList, "|,");
+
+		for (auto animIndex = 0; animIndex < animationList.size(); ++animIndex)
+		{
+			AOSet::AOAnimation animation;
+			animation.mName = animationList[animIndex];
+			animation.mInventoryUUID = animationMap[animation.mName];
+			if (animation.mInventoryUUID.isNull())
+			{
+				LLSD args;
+				args["NAME"] = animation.mName;
+				LLNotificationsUtil::add("AOImportAnimationNotFound", args);
+				continue;
+			}
+			animation.mSortOrder = animIndex;
+			newState->mAnimations.push_back(animation);
+			isValid = true;
+		}
+	}
+
+	if (!isValid)
+	{
+		LLNotificationsUtil::add("AOImportInvalid", LLSD());
+		// NOTE: cleanup is always the same, needs streamlining
+		delete mImportSet;
+		mImportSet = nullptr;
+		mUpdatedSignal();
+		return;
+	}
+
+	// clear out set UUID so processImport() knows we need to create a new folder for it
+	mImportSet->setInventoryUUID(LLUUID::null);
+	mTimerCollection.enableImportTimer(true);
+	mImportRetryCount = 0;
+	processImport(false);
+}
+
+void AOEngine::processImport(bool from_timer)
+{
+	if (mImportSet->getInventoryUUID().isNull())
+	{
+		// create new inventory folder for this AO set, the next timer tick should pick it up
+		addSet(mImportSet->getName(), [this](const LLUUID& new_cat_id)
+		{
+			mImportSet->setInventoryUUID(new_cat_id);
+		}, false);
+
+		mImportRetryCount++;
+
+		// if it takes this long to create a new inventoey category, there might be something wrong,
+		// so give some user feedback at first
+		if (mImportRetryCount == 2)
+		{
+			LLSD args;
+			args["NAME"] = mImportSet->getName();
+			LLNotificationsUtil::add("AOImportRetryCreateSet", args);
+			return;
+		}
+		// by now there clearly is something wrong, so stop trying
+		else if (mImportRetryCount == 5)
+		{
+			// NOTE: cleanup is the same as at the end of this function. Needs streamlining.
+			mTimerCollection.enableImportTimer(false);
+			delete mImportSet;
+			mImportSet = nullptr;
+			mUpdatedSignal();
+			LLSD args;
+			args["NAME"] = mImportSet->getName();
+			LLNotificationsUtil::add("AOImportAbortCreateSet", args);
+		}
+
+		return;
+	}
+
+	bool allComplete = true;
+	for (S32 index = 0; index < AOSet::AOSTATES_MAX; ++index)
+	{
+		AOSet::AOState* state = mImportSet->getState(index);
+		if (!state->mAnimations.empty())
+		{
+			allComplete = false;
+			LL_DEBUGS("AOEngine") << "state " << state->mName << " still has animations to link." << LL_ENDL;
+
+			gInventory.createNewCategory(mImportSet->getInventoryUUID(), LLFolderType::FT_NONE, state->mName, [this, state](const LLUUID& new_cat_id)
+			{
+				LL_DEBUGS("AOEngine") << "new_cat_id: " << new_cat_id << LL_ENDL;
+				state->mInventoryUUID = new_cat_id;
+
+				S32 animationIndex = state->mAnimations.size() - 1;
+				while (!state->mAnimations.empty())
+				{
+					LL_DEBUGS("AOEngine") << "linking animation " << state->mAnimations[animationIndex].mName << LL_ENDL;
+					if (createAnimationLink(state, gInventory.getItem(state->mAnimations[animationIndex].mInventoryUUID)))
+					{
+						LL_DEBUGS("AOEngine") << "link success, size " << state->mAnimations.size() << ", removing animation "
+							<< state->mAnimations[animationIndex].mName << " from import state" << LL_ENDL;
+						state->mAnimations.pop_back();
+						LL_DEBUGS("AOEngine") << "deleted, size now: " << state->mAnimations.size() << LL_ENDL;
+					}
+					else
+					{
+						LLSD args;
+						args["NAME"] = state->mAnimations[animationIndex].mName;
+						LLNotificationsUtil::add("AOImportLinkFailed", args);
+					}
+					animationIndex--;
+				}
+
+				LL_DEBUGS("AOEngine") << "exiting lambda" << LL_ENDL;
+			});
+		}
+	}
+
+	if (allComplete)
+	{
+		mTimerCollection.enableImportTimer(false);
+		mOldImportSets.push_back(mImportSet); //<ND/> FIRE-3801; Cannot delete here, or LLInstanceTracker gets upset. Just remember and delete mOldImportSets once we can. 
+		mImportSet = nullptr;
+		reload(from_timer);
+		LLNotificationsUtil::add("AOImportComplete");
+	}
+}
+
+const LLUUID& AOEngine::getAOFolder() const
+{
+	return mAOFolder;
+}
+
+void AOEngine::onRegionChange()
+{
+	// do nothing if the AO is off
+	if (!mEnabled)
+	{
+		return;
+	}
+
+	// catch errors without crashing
+	if (!mCurrentSet)
+	{
+		LL_DEBUGS("AOEngine") << "Current set was NULL" << LL_ENDL;
+		return;
+	}
+
+	// sitting needs special attention
+	if (mLastMotion == ANIM_AGENT_SIT)
+	{
+		// do nothing if sit overrides was disabled
+		if (!mCurrentSet->getSitOverride())
+		{
+			return;
+		}
+
+		// do nothing if the last overridden motion wasn't a sit.
+		// happens when sit override is enabled but there were no
+		// sit animations added to the set yet
+		if (mLastOverriddenMotion != ANIM_AGENT_SIT)
+		{
+			return;
+		}
+
+		AOSet::AOState* state = mCurrentSet->getState(AOSet::Sitting);
+		if (!state)
+		{
+			return;
+		}
+
+		// do nothing if no AO animation is playing (e.g. smart sit cancel)
+		if (LLUUID animation = state->mCurrentAnimationID; animation.isNull())
+		{
+			return;
+		}
+	}
+
+	// restart current animation on region crossing
+	gAgent.sendAnimationRequest(mLastMotion, ANIM_REQUEST_START);
+}
+
+// ----------------------------------------------------
+
+AOSitCancelTimer::AOSitCancelTimer()
+:	LLEventTimer(0.1f),
+	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)
+	{
+		LL_DEBUGS("AOEngine") << "Inventory timer tick()" << LL_ENDL;
+		AOEngine::instance().tick();
+	}
+	if (mSettingsTimer)
+	{
+		LL_DEBUGS("AOEngine") << "Settings timer tick()" << LL_ENDL;
+		AOEngine::instance().saveSettings();
+	}
+	if (mReloadTimer)
+	{
+		LL_DEBUGS("AOEngine") << "Reload timer tick()" << LL_ENDL;
+		AOEngine::instance().reload(true);
+	}
+	if (mImportTimer)
+	{
+		LL_DEBUGS("AOEngine") << "Import timer tick()" << LL_ENDL;
+		AOEngine::instance().processImport(true);
+	}
+
+	// always return FALSE or the LLEventTimer will be deleted -> crash
+	return FALSE;
+}
+
+void AOTimerCollection::enableInventoryTimer(bool enable)
+{
+	mInventoryTimer = enable;
+	updateTimers();
+}
+
+void AOTimerCollection::enableSettingsTimer(bool enable)
+{
+	mSettingsTimer = enable;
+	updateTimers();
+}
+
+void AOTimerCollection::enableReloadTimer(bool enable)
+{
+	mReloadTimer = enable;
+	updateTimers();
+}
+
+void AOTimerCollection::enableImportTimer(bool enable)
+{
+	mImportTimer = enable;
+	updateTimers();
+}
+
+void AOTimerCollection::updateTimers()
+{
+	if (!mInventoryTimer && !mSettingsTimer && !mReloadTimer && !mImportTimer)
+	{
+		LL_DEBUGS("AOEngine") << "no timer needed, stopping internal timer." << LL_ENDL;
+		mEventTimer.stop();
+	}
+	else
+	{
+		LL_DEBUGS("AOEngine") << "timer needed, starting internal timer." << LL_ENDL;
+		mEventTimer.start();
+	}
+}
diff --git a/indra/newview/aoengine.h b/indra/newview/aoengine.h
new file mode 100644
index 00000000000..aa4cbf7e43a
--- /dev/null
+++ b/indra/newview/aoengine.h
@@ -0,0 +1,234 @@
+/**
+ * @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
+ * $/LicenseInfo$
+ */
+
+#ifndef AOENGINE_H
+#define AOENGINE_H
+
+#define ROOT_AO_FOLDER			"Animation Overrides"
+
+#include "aoset.h"
+
+#include "llassettype.h"
+#include "lleventtimer.h"
+#include "llextendedstatus.h"
+#include "llsingleton.h"
+#include "llviewerinventory.h"
+#include <boost/signals2.hpp>
+
+class AOTimerCollection
+:	public LLEventTimer
+{
+	public:
+		AOTimerCollection();
+		~AOTimerCollection();
+
+		virtual BOOL tick();
+
+		void enableInventoryTimer(bool enable);
+		void enableSettingsTimer(bool enable);
+		void enableReloadTimer(bool enable);
+		void enableImportTimer(bool enable);
+
+	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<AOEngine>
+{
+	LLSINGLETON(AOEngine);
+	~AOEngine();
+
+	public:
+		enum eCycleMode
+		{
+			CycleAny,
+			CycleNext,
+			CyclePrevious
+		};
+
+		void enable(bool enable);
+		void enableStands(bool enable_stands);
+		const LLUUID override(const LLUUID& motion, bool start);
+		void tick();
+		void update();
+		void reload(bool);
+		void reloadStateAnimations(AOSet::AOState* state);
+		void clear(bool from_timer);
+
+		const LLUUID& getAOFolder() const;
+
+		void addSet(const std::string& name, inventory_func_type callback, bool reload = true);
+		bool removeSet(AOSet* set);
+
+		void 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();
+		void checkBelowWater(bool check_underwater);
+
+		bool importNotecard(const LLInventoryItem* item);
+		void processImport(bool from_timer);
+
+		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 mouselook);
+		void selectSet(AOSet* set);
+		AOSet* selectSetByName(const std::string& name);
+		AOSet* getSetByName(const std::string& name) const;
+
+		// callback from LLAppViewer
+		static void onLoginComplete();
+
+		const std::vector<AOSet*> getSetList() const;
+		const std::string getCurrentSetName() const;
+		const AOSet* getDefaultSet() const;
+		bool renameSet(AOSet* set, const std::string& name);
+
+		void setDefaultSet(AOSet* set);
+		void setOverrideSits(AOSet* set, bool override_sit);
+		void setSmart(AOSet* set, bool smart);
+		void setDisableMouselookStands(AOSet* set, bool disabled);
+		void setCycle(AOSet::AOState* set, bool cycle);
+		void setRandomize(AOSet::AOState* state, bool randomize);
+		void setCycleTime(AOSet::AOState* state, F32 time);
+
+		void saveSettings();
+
+		typedef boost::signals2::signal<void ()> updated_signal_t;
+		boost::signals2::connection setReloadCallback(const updated_signal_t::slot_type& cb)
+		{
+			return mUpdatedSignal.connect(cb);
+		};
+
+		typedef boost::signals2::signal<void (const LLUUID&)> animation_changed_signal_t;
+		boost::signals2::connection setAnimationChangedCallback(const animation_changed_signal_t::slot_type& cb)
+		{
+			return mAnimationChangedSignal.connect(cb);
+		};
+
+		typedef boost::signals2::signal<void(const std::string&)> set_changed_signal_t;
+		boost::signals2::connection setSetChangedCallback(const set_changed_signal_t::slot_type& cb)
+		{
+			return mSetChangedSignal.connect(cb);
+		}
+
+	protected:
+		void init();
+
+		void setLastMotion(const LLUUID& motion);
+		void setLastOverriddenMotion(const LLUUID& motion);
+		void setStateCycleTimer(const AOSet::AOState* state);
+
+		void stopAllStandVariants();
+		void stopAllSitVariants();
+
+		bool foreignAnimations();
+		AOSet::AOState* mapSwimming(const LLUUID& motion) const;
+		AOSet::AOState* getStateForMotion(const LLUUID& motion) const;
+
+		void updateSortOrder(AOSet::AOState* state);
+		void saveSet(const AOSet* set);
+		void saveState(const AOSet::AOState* state);
+
+		bool createAnimationLink(AOSet::AOState* state, const LLInventoryItem* item);
+		bool findForeignItems(const LLUUID& uuid) const;
+		void purgeFolder(const LLUUID& uuid) const;
+
+		void onRegionChange();
+
+		void onToggleAOControl();
+		void onToggleAOStandsControl();
+		void onPauseAO();
+
+		static void onNotecardLoadComplete(const LLUUID& assetUUID, LLAssetType::EType type,
+												void* userdata, S32 status, LLExtStat extStatus);
+		void parseNotecard(const char* buffer);
+
+		updated_signal_t mUpdatedSignal;
+		animation_changed_signal_t mAnimationChangedSignal;
+		set_changed_signal_t mSetChangedSignal;
+
+		AOTimerCollection mTimerCollection;
+		AOSitCancelTimer mSitCancelTimer;
+
+		bool mEnabled;
+		bool mEnabledStands;
+		bool mInMouselook;
+		bool mUnderWater;
+
+		LLUUID mAOFolder;
+		LLUUID mLastMotion;
+		LLUUID mLastOverriddenMotion;
+		LLUUID mTransitionId;
+
+		// this motion will be ignored once in the overrider when stopping, fixes a case
+		// where the AO doesn't correctly start up on login or when getting enabled manually
+		LLUUID mIgnoreMotionStopOnce;
+
+		std::vector<AOSet*> mSets;
+		std::vector<AOSet*> mOldSets;
+		AOSet* mCurrentSet;
+		AOSet* mDefaultSet;
+
+		AOSet* mImportSet;
+		std::vector<AOSet*> mOldImportSets;
+		S32 mImportRetryCount;
+
+		boost::signals2::connection mRegionChangeConnection;
+};
+
+#endif // AOENGINE_H
diff --git a/indra/newview/alaoset.cpp b/indra/newview/aoset.cpp
similarity index 52%
rename from indra/newview/alaoset.cpp
rename to indra/newview/aoset.cpp
index c3598b3461f..52ab2a07de6 100644
--- a/indra/newview/alaoset.cpp
+++ b/indra/newview/aoset.cpp
@@ -1,12 +1,10 @@
 /**
- * @file alaoset.cpp
+ * @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
- * Copyright (C) 2016, Cinder <cinder@sdf.org>
- * Copyright (C) 2020, Rye Mutt <rye@alchemyviewer.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -26,29 +24,29 @@
 
 #include "llviewerprecompiledheaders.h"
 
-#include "alaoengine.h"
-#include "alaoset.h"
+#include "aoengine.h"
+#include "aoset.h"
 #include "llanimationstates.h"
+#include "llinventorymodel.h"
 
-static const std::string sDefaultSetName = "** New AO Set **";
-
-ALAOSet::ALAOSet(const LLUUID& inventoryID)
-:	LLEventTimer(10000.0f)
-,	mInventoryID(inventoryID)
-,	mName(sDefaultSetName)
-,	mSitOverride(false)
-,	mSmart(false)
-,	mMouselookDisable(false)
-,	mComplete(false)
-,	mCurrentMotion(LLUUID())
-,	mDirty(false)
+AOSet::AOSet(const LLUUID inventoryID)
+:	LLEventTimer(10000.0f),
+	mInventoryID(inventoryID),
+	mName("** New AO Set **"),
+	mSitOverride(false),
+	mSmart(false),
+	mMouselookStandDisable(false),
+	mComplete(false),
+	mDirty(false),
+	mCurrentMotion(LLUUID())
 {
 	LL_DEBUGS("AOEngine") << "Creating new AO set: " << this << LL_ENDL;
 
 	// ZHAO names first, alternate names following, separated by | characters
 	// keep number and order in sync with the enum in the declaration
-	static const std::array<std::string, AOSTATES_MAX> sStateNames {{
-		"Standing|Standing mode 1|Stand.1|Stand.2|Stand.3",
+	const std::string stateNames[AOSTATES_MAX]=
+	{
+		"Standing|Stand.1|Stand.2|Stand.3",
 		"Walking|Walk.N",
 		"Running",
 		"Sitting|Sit.N",
@@ -73,10 +71,11 @@ ALAOSet::ALAOSet(const LLUUID& inventoryID)
 		"Swimming Forward|Swim.N",
 		"Swimming Up|Swim.U",
 		"Swimming Down|Swim.D"
-	}};
+	};
 
 	// keep number and order in sync with the enum in the declaration
-	static const std::array<LLUUID, AOSTATES_MAX> sStateUUIDs {{
+	const LLUUID stateUUIDs[AOSTATES_MAX]=
+	{
 		ANIM_AGENT_STAND,
 		ANIM_AGENT_WALK,
 		ANIM_AGENT_RUN,
@@ -102,16 +101,16 @@ ALAOSet::ALAOSet(const LLUUID& inventoryID)
 		ANIM_AGENT_FLY,			// needs special treatment
 		ANIM_AGENT_HOVER_UP,	// needs special treatment
 		ANIM_AGENT_HOVER_DOWN	// needs special treatment
-	}};
+	};
 
 	for (S32 index = 0; index < AOSTATES_MAX; ++index)
 	{
 		std::vector<std::string> stateNameList;
-		LLStringUtil::getTokens(sStateNames[index], stateNameList, "|");
+		LLStringUtil::getTokens(stateNames[index], stateNameList, "|");
 
 		mStates[index].mName = stateNameList[0];			// for quick reference
 		mStates[index].mAlternateNames = stateNameList;		// to get all possible names, including mName
-		mStates[index].mRemapID = sStateUUIDs[index];
+		mStates[index].mRemapID = stateUUIDs[index];
 		mStates[index].mInventoryUUID = LLUUID::null;
 		mStates[index].mCurrentAnimation = 0;
 		mStates[index].mCurrentAnimationID = LLUUID::null;
@@ -119,27 +118,27 @@ ALAOSet::ALAOSet(const LLUUID& inventoryID)
 		mStates[index].mRandom = false;
 		mStates[index].mCycleTime = 0.0f;
 		mStates[index].mDirty = false;
-		mStateNames.push_back(stateNameList[0]);
+		mStateNames.emplace_back(stateNameList[0]);
 	}
 	stopTimer();
 }
 
-ALAOSet::~ALAOSet()
+AOSet::~AOSet()
 {
 	LL_DEBUGS("AOEngine") << "Set deleted: " << this << LL_ENDL;
 }
 
-ALAOSet::AOState* ALAOSet::getState(S32 name)
+AOSet::AOState* AOSet::getState(S32 eName)
 {
-	return &mStates[name];
+	return &mStates[eName];
 }
 
-ALAOSet::AOState* ALAOSet::getStateByName(const std::string& name)
+AOSet::AOState* AOSet::getStateByName(const std::string& name)
 {
 	for (S32 index = 0; index < AOSTATES_MAX; ++index)
 	{
 		AOState* state = &mStates[index];
-		for (U32 names = 0; names < state->mAlternateNames.size(); ++names)
+		for (auto names = 0; names < state->mAlternateNames.size(); ++names)
 		{
 			if (state->mAlternateNames[names].compare(name) == 0)
 			{
@@ -150,7 +149,7 @@ ALAOSet::AOState* ALAOSet::getStateByName(const std::string& name)
 	return nullptr;
 }
 
-ALAOSet::AOState* ALAOSet::getStateByRemapID(const LLUUID& id)
+AOSet::AOState* AOSet::getStateByRemapID(const LLUUID& id)
 {
 	LLUUID remap_id = id;
 	if (remap_id == ANIM_AGENT_SIT_GROUND)
@@ -168,40 +167,59 @@ ALAOSet::AOState* ALAOSet::getStateByRemapID(const LLUUID& id)
 	return nullptr;
 }
 
-const LLUUID& ALAOSet::getAnimationForState(AOState* state) const
+const LLUUID& AOSet::getAnimationForState(AOState* state) const
 {
-	if (!state) return LLUUID::null;
-	
-	size_t num_animations = state->mAnimations.size();
-	if (num_animations)
+	if (state)
 	{
-		if (state->mCycle)
+		auto numOfAnimations = state->mAnimations.size();
+		if (numOfAnimations)
 		{
-			if (state->mRandom)
+			if (state->mCycle)
 			{
-				state->mCurrentAnimation = ll_frand() * num_animations;
-				LL_DEBUGS("AOEngine") << "randomly chosen " << state->mCurrentAnimation << " of " << num_animations << LL_ENDL;
+				if (state->mRandom)
+				{
+					state->mCurrentAnimation = ll_frand() * numOfAnimations;
+					LL_DEBUGS("AOEngine") << "randomly chosen " << state->mCurrentAnimation << " of " << numOfAnimations << LL_ENDL;
+				}
+				else
+				{
+					state->mCurrentAnimation++;
+					if (state->mCurrentAnimation >= state->mAnimations.size())
+					{
+						state->mCurrentAnimation = 0;
+					}
+					LL_DEBUGS("AOEngine") << "cycle " << state->mCurrentAnimation << " of " << numOfAnimations << LL_ENDL;
+				}
 			}
-			else
+
+			AOAnimation& anim = state->mAnimations[state->mCurrentAnimation];
+
+			if (anim.mAssetUUID.isNull())
 			{
-				state->mCurrentAnimation++;
-				if (state->mCurrentAnimation >= state->mAnimations.size())
+				LL_DEBUGS("AOEngine") << "Asset UUID for chosen animation " << anim.mName << " not yet known, try to find it." << LL_ENDL;
+
+				if(LLViewerInventoryItem* item = gInventory.getItem(anim.mInventoryUUID) ; item)
+				{
+					LL_DEBUGS("AOEngine") << "Found asset UUID for chosen animation: " << item->getAssetUUID() << " - Updating AOAnimation.mAssetUUID" << LL_ENDL;
+					anim.mAssetUUID = item->getAssetUUID();
+				}
+				else
 				{
-					state->mCurrentAnimation = 0;
+					LL_DEBUGS("AOEngine") << "Inventory UUID " << anim.mInventoryUUID << " for chosen animation " << anim.mName << " still returns no asset." << LL_ENDL;
 				}
-				LL_DEBUGS("AOEngine") << "cycle " << state->mCurrentAnimation << " of " << num_animations << LL_ENDL;
 			}
+
+			return anim.mAssetUUID;
+		}
+		else
+		{
+			LL_DEBUGS("AOEngine") << "animation state has no animations assigned" << LL_ENDL;
 		}
-		return state->mAnimations[state->mCurrentAnimation].mAssetUUID;
-	}
-	else
-	{
-		LL_DEBUGS("AOEngine") << "animation state has no animations assigned" << LL_ENDL;
 	}
 	return LLUUID::null;
 }
 
-void ALAOSet::startTimer(F32 timeout)
+void AOSet::startTimer(F32 timeout)
 {
 	mEventTimer.stop();
 	mPeriod = timeout;
@@ -209,14 +227,94 @@ void ALAOSet::startTimer(F32 timeout)
 	LL_DEBUGS("AOEngine") << "Starting state timer for " << getName() << " at " << timeout << LL_ENDL;
 }
 
-void ALAOSet::stopTimer()
+void AOSet::stopTimer()
 {
 	LL_DEBUGS("AOEngine") << "State timer for " << getName() << " stopped." << LL_ENDL;
 	mEventTimer.stop();
 }
 
-BOOL ALAOSet::tick()
+BOOL AOSet::tick()
 {
-	ALAOEngine::instance().cycleTimeout(this);
+	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(const std::string& name)
+{
+	mName=name;
+}
+
+bool AOSet::getSitOverride() const
+{
+	return mSitOverride;
+}
+
+void AOSet::setSitOverride(bool override_sit)
+{
+	mSitOverride = override_sit;
+}
+
+bool AOSet::getSmart() const
+{
+	return mSmart;
+}
+
+void AOSet::setSmart(bool smart)
+{
+	mSmart = smart;
+}
+
+bool AOSet::getMouselookStandDisable() const
+{
+	return mMouselookStandDisable;
+}
+
+void AOSet::setMouselookStandDisable(bool disable)
+{
+	mMouselookStandDisable = disable;
+}
+
+bool AOSet::getComplete() const
+{
+	return mComplete;
+}
+
+void AOSet::setComplete(bool complete)
+{
+	mComplete = complete;
+}
+
+bool AOSet::getDirty() const
+{
+	return mDirty;
+}
+
+void AOSet::setDirty(bool dirty)
+{
+	mDirty = dirty;
+}
+
+void AOSet::setMotion(const LLUUID& motion)
+{
+	mCurrentMotion = motion;
+}
+
+const LLUUID& AOSet::getMotion() const
+{
+	return mCurrentMotion;
+}
diff --git a/indra/newview/aoset.h b/indra/newview/aoset.h
new file mode 100644
index 00000000000..878b36cedd6
--- /dev/null
+++ b/indra/newview/aoset.h
@@ -0,0 +1,145 @@
+/**
+ * @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
+ * $/LicenseInfo$
+ */
+
+#ifndef AOSET_H
+#define AOSET_H
+
+#include "lleventtimer.h"
+
+class LLInventoryItem;
+
+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,
+			SoftLanding,
+			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;
+			LLUUID mOriginalUUID;
+			S32 mSortOrder;
+		};
+
+		struct AOState
+		{
+			std::string mName;
+			std::vector<std::string> mAlternateNames;
+			std::vector<const LLInventoryItem*> mAddQueue;
+			LLUUID mRemapID;
+			bool mCycle;
+			bool mRandom;
+			S32 mCycleTime;
+			std::vector<AOAnimation> mAnimations;
+			U32 mCurrentAnimation;
+			LLUUID mCurrentAnimationID;
+			LLUUID mInventoryUUID;
+			bool mDirty;
+		};
+
+		const LLUUID& getInventoryUUID() const;
+		void setInventoryUUID(const LLUUID& inventoryID);
+
+		const std::string& getName() const;
+		void setName(const std::string& name);
+
+		bool getSitOverride() const;
+		void setSitOverride(bool override_sit);
+
+		bool getSmart() const;
+		void setSmart(bool smart);
+
+		bool getMouselookStandDisable() const;
+		void setMouselookStandDisable(bool disable);
+
+		bool getComplete() const;
+		void setComplete(bool complete);
+
+		const LLUUID& getMotion() const;
+		void setMotion(const LLUUID& motion);
+
+		bool getDirty() const;
+		void setDirty(bool dirty);
+
+		AOState* getState(S32 eName);
+		AOState* getStateByName(const std::string& name);
+		AOState* getStateByRemapID(const LLUUID& id);
+		const LLUUID& getAnimationForState(AOState* state) const;
+
+		void startTimer(F32 timeout);
+		void stopTimer();
+		virtual BOOL tick();
+
+		std::vector<std::string> mStateNames;
+
+	protected:
+		LLUUID mInventoryID;
+
+		std::string mName;
+		bool mSitOverride;
+		bool mSmart;
+		bool mMouselookStandDisable;
+		bool mComplete;
+		LLUUID mCurrentMotion;
+		bool mDirty;
+
+		AOState mStates[AOSTATES_MAX];
+};
+
+#endif // AOSET_H
diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml
index 7bea0e194c4..a8c71425a28 100644
--- a/indra/newview/app_settings/commands.xml
+++ b/indra/newview/app_settings/commands.xml
@@ -12,6 +12,16 @@
            is_running_function="Floater.IsOpen"
            is_running_parameters="about_land"
            />
+  <command name="ao"
+           available_in_toybox="true"
+           icon="Command_Move_Icon"
+           label_ref="Command_AnimationOverride_Label"
+           tooltip_ref="Command_AnimationOverride_Tooltip"
+           execute_function="Floater.ToggleOrBringToFront"
+           execute_parameters="ao"
+           is_running_function="Floater.IsOpen"
+           is_running_parameters="ao"
+           />
   <command name="appearance"  
            available_in_toybox="true"
            icon="Command_Appearance_Icon"
diff --git a/indra/newview/app_settings/settings_per_account_alchemy.xml b/indra/newview/app_settings/settings_per_account_alchemy.xml
index b9c20d99871..6da3284d14c 100644
--- a/indra/newview/app_settings/settings_per_account_alchemy.xml
+++ b/indra/newview/app_settings/settings_per_account_alchemy.xml
@@ -1,26 +1,84 @@
 <llsd>
   <map>
-    <key>AlchemyAOEnable</key>
-    <map>
-      <key>Comment</key>
-      <string>Use Animation Overrides</string>
-      <key>Persist</key>
-      <integer>1</integer>
-      <key>Type</key>
-      <string>Boolean</string>
-      <key>Value</key>
-      <integer>0</integer>
-    </map>
-    <key>ALDiscordIntegration</key>
-    <map>
-      <key>Comment</key>
-      <string>Enable discord integration</string>
-      <key>Persist</key>
-      <integer>1</integer>
-      <key>Type</key>
-      <string>Boolean</string>
-      <key>Value</key>
-      <integer>1</integer>
+	<key>AlchemyAOEnable</key>
+	<map>
+		<key>Comment</key>
+		<string>Use the viewer side Animation Overrider</string>
+		<key>Persist</key>
+		<integer>1</integer>
+		<key>Type</key>
+		<string>Boolean</string>
+		<key>Value</key>
+		<integer>0</integer>
+	</map>
+	<key>PauseAO</key>
+	<map>
+		<key>Comment</key>
+		<string>Pause the viewer side Animation Overrider</string>
+		<key>Persist</key>
+		<integer>0</integer>
+		<key>Type</key>
+		<string>Boolean</string>
+		<key>Value</key>
+		<integer>0</integer>
+	</map>
+	<key>UseAOStands</key>
+	<map>
+		<key>Comment</key>
+		<string>Use the viewer side Animation Overrider for standing and turning animations when enabled</string>
+		<key>Persist</key>
+		<integer>0</integer>
+		<key>Type</key>
+		<string>Boolean</string>
+		<key>Value</key>
+		<integer>1</integer>
+	</map>
+	<key>UseFullAOInterface</key>
+	<map>
+		<key>Comment</key>
+		<string>Use the full Animation Overrider interface (TRUE) or the small, reduced interface (FALSE).</string>
+		<key>Persist</key>
+		<integer>1</integer>
+		<key>Type</key>
+		<string>Boolean</string>
+		<key>Value</key>
+		<integer>0</integer>
+	</map>
+	<key>LockAOFolders</key>
+	<map>
+		<key>Comment</key>
+		<string>Keep the AO folders in Inventory safe from manual changes.</string>
+		<key>Type</key>
+		<string>Boolean</string>
+		<key>Value</key>
+		<integer>1</integer>
+	</map>
+	<key>floater_rect_ao_full</key>
+	<map>
+		<key>Comment</key>
+		<string>Position and size for the full Animation Overrider interface. The defaults are just dummy values.</string>
+		<key>Persist</key>
+		<integer>1</integer>
+		<key>Type</key>
+		<string>Rect</string>
+		<key>Value</key>
+		<array>
+			<integer>0</integer>
+			<integer>0</integer>
+			<integer>0</integer>
+			<integer>0</integer>
+		</array>
+	</map>
+	<key>ALDiscordIntegration</key>
+	<map>
+		<key>Comment</key>
+		<string>Enable discord integration</string>
+		<key>Persist</key>
+		<integer>1</integer>
+		<key>Type</key>
+		<string>Boolean</string>
+		<key>Value</key>
+		<integer>1</integer>
     </map>
     <key>ALDiscordShareLocationRegion</key>
     <map>
diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp
index cfaf599cebd..a704d045ba7 100644
--- a/indra/newview/llagentcamera.cpp
+++ b/indra/newview/llagentcamera.cpp
@@ -29,7 +29,7 @@
 
 #include "pipeline.h"
 
-//#include "alaoengine.h"
+#include "aoengine.h"
 #include "llagent.h"
 #include "llanimationstates.h"
 #include "llfloatercamera.h"
@@ -2442,9 +2442,9 @@ void LLAgentCamera::changeCameraToMouselook(BOOL animate)
 		
 		updateLastCamera();
 		mCameraMode = CAMERA_MODE_MOUSELOOK;
+		AOEngine::getInstance()->inMouselook(TRUE);
 		const U32 old_flags = gAgent.getControlFlags();
 		gAgent.setControlFlags(AGENT_CONTROL_MOUSELOOK);
-        //ALAOEngine::getInstance()->inMouselook(true);
 		if (old_flags != gAgent.getControlFlags())
 		{
 			gAgent.setFlagsDirty();
@@ -2509,7 +2509,7 @@ void LLAgentCamera::changeCameraToFollow(BOOL animate)
 
 		updateLastCamera();
 		mCameraMode = CAMERA_MODE_FOLLOW;
-		//ALAOEngine::getInstance()->inMouselook(false);
+		AOEngine::getInstance()->inMouselook(FALSE);
 
 		// bang-in the current focus, position, and up vector of the follow cam
 		mFollowCam.reset(mCameraPositionAgent, LLViewerCamera::getInstance()->getPointOfInterest(), LLVector3::z_axis);
@@ -2590,7 +2590,7 @@ void LLAgentCamera::changeCameraToThirdPerson(BOOL animate)
 		}
 		updateLastCamera();
 		mCameraMode = CAMERA_MODE_THIRD_PERSON;
-        //ALAOEngine::getInstance()->inMouselook(false);
+		AOEngine::getInstance()->inMouselook(FALSE);
 		gAgent.clearControlFlags(AGENT_CONTROL_MOUSELOOK);
 	}
 
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 51c8f8177e3..6cb51f79dda 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -32,6 +32,7 @@
 #include "lltransfersourceasset.h" 
 #include "llavatarnamecache.h"	// IDEVO
 
+#include "aoengine.h"
 #include "llagent.h"
 #include "llagentcamera.h"
 #include "llagentwearables.h"
@@ -3964,9 +3965,9 @@ LLFolderType::EType LLFolderBridge::getPreferredType() const
 	if(cat)
 	{
 		const std::string& cat_name(cat->getName());
-/*		if (cat_name == ROOT_AO_FOLDER)
+		if (cat_name == ROOT_AO_FOLDER)
 			preferred_type = LLFolderType::FT_ANIM_OVERRIDES;
-		else */
+		else 
 		if (cat_name == "#Firestorm" || cat_name == "#Phoenix"
 				 || cat_name == "#Kokua")
 			preferred_type = LLFolderType::FT_TOXIC;
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index f990360bd30..c8ba04708c3 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -960,12 +960,11 @@ const LLUUID LLInventoryModel::findCategoryUUIDForNameInRoot(std::string const&
 		cats = get_ptr_in_map(mParentChildCategoryTree, root_id);
 		if (cats)
 		{
-			U32 count = cats->size();
-			for (U32 i = 0; i < count; ++i)
+			for (const auto& cat : *cats)
 			{
-				if (cats->at(i)->getName() == folder_name)
+				if (cat->getName() == folder_name)
 				{
-					LLUUID const& folder_id = cats->at(i)->getUUID();
+					LLUUID const& folder_id = cat->getUUID();
 					if (rv.isNull() || folder_id < rv)
 					{
 						rv = folder_id;
diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp
index eeed08334d1..25fb8594ab9 100644
--- a/indra/newview/llstatusbar.cpp
+++ b/indra/newview/llstatusbar.cpp
@@ -29,7 +29,7 @@
 #include "llstatusbar.h"
 
 // viewer includes
-//#include "alpanelaopulldown.h"
+#include "alpanelaopulldown.h"
 #include "alpanelquicksettingspulldown.h"
 #include "llagent.h"
 #include "llagentcamera.h"
@@ -113,7 +113,7 @@ LLStatusBar::LLStatusBar(const LLRect& rect)
 	mSGPacketLoss(NULL),
 	mPanelPopupHolder(nullptr),
 	mBtnQuickSettings(nullptr),
-	//mBtnAO(nullptr),
+	mBtnAO(nullptr),
 	mBtnVolume(NULL),
 	mBoxBalance(NULL),
 	mBalance(0),
@@ -193,10 +193,10 @@ BOOL LLStatusBar::postBuild()
 	mBtnQuickSettings = getChild<LLButton>("quick_settings_btn");
 	mBtnQuickSettings->setMouseEnterCallback(boost::bind(&LLStatusBar::onMouseEnterQuickSettings, this));
 
-	//mBtnAO = getChild<LLButton>("ao_btn");
-	//mBtnAO->setClickedCallback(&LLStatusBar::onClickAOBtn, this);
-	//mBtnAO->setMouseEnterCallback(boost::bind(&LLStatusBar::onMouseEnterAO, this));
-	//mBtnAO->setToggleState(gSavedPerAccountSettings.getBOOL("AlchemyAOEnable")); // shunt it into correct state - ALCH-368
+	mBtnAO = getChild<LLButton>("ao_btn");
+	mBtnAO->setClickedCallback(&LLStatusBar::onClickAOBtn, this);
+	mBtnAO->setMouseEnterCallback(boost::bind(&LLStatusBar::onMouseEnterAO, this));
+	mBtnAO->setToggleState(gSavedPerAccountSettings.getBOOL("AlchemyAOEnable")); // shunt it into correct state - ALCH-368
 
 	mBtnVolume = getChild<LLButton>( "volume_btn" );
 	mBtnVolume->setClickedCallback(&LLStatusBar::onClickVolume, this );
@@ -210,7 +210,7 @@ BOOL LLStatusBar::postBuild()
 	LLHints::getInstance()->registerHintTarget("linden_balance", mBalanceBG->getHandle());
 
 	gSavedSettings.getControl("MuteAudio")->getSignal()->connect(boost::bind(&LLStatusBar::onVolumeChanged, this, _2));
-	//gSavedPerAccountSettings.getControl("AlchemyAOEnable")->getCommitSignal()->connect(boost::bind(&LLStatusBar::onAOStateChanged, this));
+	gSavedPerAccountSettings.getControl("AlchemyAOEnable")->getCommitSignal()->connect(boost::bind(&LLStatusBar::onAOStateChanged, this));
 
 	mTextFPS = getChild<LLTextBox>("FPSText");
 
@@ -277,10 +277,10 @@ BOOL LLStatusBar::postBuild()
 	mPanelVolumePulldown->setFollows(FOLLOWS_TOP|FOLLOWS_RIGHT);
 	mPanelVolumePulldown->setVisible(FALSE);
 
-	//mPanelAOPulldown = new ALPanelAOPulldown();
-	//addChild(mPanelAOPulldown);
-	//mPanelAOPulldown->setFollows(FOLLOWS_TOP | FOLLOWS_RIGHT);
-	//mPanelAOPulldown->setVisible(FALSE);
+	mPanelAOPulldown = new ALPanelAOPulldown();
+	addChild(mPanelAOPulldown);
+	mPanelAOPulldown->setFollows(FOLLOWS_TOP | FOLLOWS_RIGHT);
+	mPanelAOPulldown->setVisible(FALSE);
 
 	mPanelQuickSettingsPulldown = new ALPanelQuickSettingsPulldown();
 	addChild(mPanelQuickSettingsPulldown);
@@ -402,7 +402,7 @@ void LLStatusBar::setVisibleForMouselook(bool visible)
 	mBalanceBG->setVisible(visible);
 	mBoxBalance->setVisible(visible);
 	mBtnQuickSettings->setVisible(visible);
-	//mBtnAO->setVisible(visible);
+	mBtnAO->setVisible(visible);
 	mBtnVolume->setVisible(visible);
 	mMediaToggle->setVisible(visible);
 	mSGBandwidth->setVisible(visible && show_net_stats);
@@ -585,7 +585,7 @@ void LLStatusBar::onMouseEnterPresetsCamera()
 	mPanelNearByMedia->setVisible(FALSE);
 	mPanelVolumePulldown->setVisible(FALSE);
 	mPanelPresetsPulldown->setVisible(FALSE);
-    //mPanelAOPulldown->setVisible(FALSE);
+    mPanelAOPulldown->setVisible(FALSE);
     // mPanelAvatarComplexityPulldown->setVisible(FALSE);
     mPanelQuickSettingsPulldown->setVisible(FALSE);
 	mPanelPresetsCameraPulldown->setVisible(TRUE);
@@ -612,7 +612,7 @@ void LLStatusBar::onMouseEnterPresets()
 	mPanelPresetsCameraPulldown->setVisible(FALSE);
 	mPanelNearByMedia->setVisible(FALSE);
 	mPanelVolumePulldown->setVisible(FALSE);
-    //mPanelAOPulldown->setVisible(FALSE);
+    mPanelAOPulldown->setVisible(FALSE);
     // mPanelAvatarComplexityPulldown->setVisible(FALSE);
     mPanelQuickSettingsPulldown->setVisible(FALSE);
 	mPanelPresetsPulldown->setVisible(TRUE);
@@ -639,35 +639,35 @@ void LLStatusBar::onMouseEnterQuickSettings()
     mPanelPresetsPulldown->setVisible(FALSE);
 	mPanelNearByMedia->setVisible(FALSE);
 	mPanelVolumePulldown->setVisible(FALSE);
-	//mPanelAOPulldown->setVisible(FALSE);
+	mPanelAOPulldown->setVisible(FALSE);
 	//mPanelAvatarComplexityPulldown->setVisible(FALSE);
 	mPanelQuickSettingsPulldown->setVisible(TRUE);
 }
 
-//void LLStatusBar::onMouseEnterAO()
-//{
-//	LLRect qs_rect = mPanelAOPulldown->getRect();
-//	LLRect qs_btn_rect = mBtnAO->getRect();
-//	qs_rect.setLeftTopAndSize(qs_btn_rect.mLeft -
-//							  (qs_rect.getWidth() - qs_btn_rect.getWidth()) / 2,
-//							  qs_btn_rect.mBottom,
-//							  qs_rect.getWidth(),
-//							  qs_rect.getHeight());
-//	// force onscreen
-//	qs_rect.translate(mPanelPopupHolder->getRect().getWidth() - qs_rect.mRight, 0);
-//	
-//	mPanelAOPulldown->setShape(qs_rect);
-//	LLUI::getInstance()->clearPopups();
-//	LLUI::getInstance()->addPopup(mPanelAOPulldown);
-//	
-//	mPanelPresetsCameraPulldown->setVisible(FALSE);
-//    mPanelPresetsPulldown->setVisible(FALSE);
-//	mPanelNearByMedia->setVisible(FALSE);
-//	mPanelVolumePulldown->setVisible(FALSE);
-//	mPanelQuickSettingsPulldown->setVisible(FALSE);
-//	mPanelAOPulldown->setVisible(TRUE);
-//	//mPanelAvatarComplexityPulldown->setVisible(FALSE);
-//}
+void LLStatusBar::onMouseEnterAO()
+{
+	LLRect qs_rect = mPanelAOPulldown->getRect();
+	LLRect qs_btn_rect = mBtnAO->getRect();
+	qs_rect.setLeftTopAndSize(qs_btn_rect.mLeft -
+							  (qs_rect.getWidth() - qs_btn_rect.getWidth()) / 2,
+							  qs_btn_rect.mBottom,
+							  qs_rect.getWidth(),
+							  qs_rect.getHeight());
+	// force onscreen
+	qs_rect.translate(mPanelPopupHolder->getRect().getWidth() - qs_rect.mRight, 0);
+	
+	mPanelAOPulldown->setShape(qs_rect);
+	LLUI::getInstance()->clearPopups();
+	LLUI::getInstance()->addPopup(mPanelAOPulldown);
+	
+	mPanelPresetsCameraPulldown->setVisible(FALSE);
+    mPanelPresetsPulldown->setVisible(FALSE);
+	mPanelNearByMedia->setVisible(FALSE);
+	mPanelVolumePulldown->setVisible(FALSE);
+	mPanelQuickSettingsPulldown->setVisible(FALSE);
+	mPanelAOPulldown->setVisible(TRUE);
+	//mPanelAvatarComplexityPulldown->setVisible(FALSE);
+}
 
 void LLStatusBar::onMouseEnterVolume()
 {
@@ -691,7 +691,7 @@ void LLStatusBar::onMouseEnterVolume()
 	mPanelPresetsPulldown->setVisible(FALSE);
 	mPanelNearByMedia->setVisible(FALSE);
 	mPanelQuickSettingsPulldown->setVisible(FALSE);
-	//mPanelAOPulldown->setVisible(FALSE);
+	mPanelAOPulldown->setVisible(FALSE);
 	mPanelVolumePulldown->setVisible(TRUE);
 }
 
@@ -717,7 +717,7 @@ void LLStatusBar::onMouseEnterNearbyMedia()
 	mPanelPresetsPulldown->setVisible(FALSE);
 	mPanelQuickSettingsPulldown->setVisible(FALSE);
 	mPanelVolumePulldown->setVisible(FALSE);
-	//mPanelAOPulldown->setVisible(FALSE);
+	mPanelAOPulldown->setVisible(FALSE);
 	mPanelNearByMedia->setVisible(TRUE);
 }
 
@@ -753,10 +753,10 @@ void LLStatusBar::onClickMediaToggle(void* data)
 	LLViewerMedia::getInstance()->setAllMediaPaused(pause);
 }
 
-//void LLStatusBar::onAOStateChanged()
-//{
-//	mBtnAO->setToggleState(gSavedPerAccountSettings.getBOOL("AlchemyAOEnable"));
-//}
+void LLStatusBar::onAOStateChanged()
+{
+	mBtnAO->setToggleState(gSavedPerAccountSettings.getBOOL("AlchemyAOEnable"));
+}
 
 BOOL can_afford_transaction(S32 cost)
 {
diff --git a/indra/newview/llstatusbar.h b/indra/newview/llstatusbar.h
index bc58f444cdd..f447bf3b66d 100644
--- a/indra/newview/llstatusbar.h
+++ b/indra/newview/llstatusbar.h
@@ -106,7 +106,7 @@ class LLStatusBar final
 	void onMouseEnterPresetsCamera();
 	void onMouseEnterPresets();
 	void onMouseEnterQuickSettings();
-	//void onMouseEnterAO();
+	void onMouseEnterAO();
 	void onMouseEnterVolume();
 	void onMouseEnterNearbyMedia();
 	void onClickScreen(S32 x, S32 y);
@@ -138,7 +138,7 @@ class LLStatusBar final
 	LLIconCtrl	*mIconPresetsCamera;
 	LLIconCtrl	*mIconPresetsGraphic;
 	LLButton	*mBtnQuickSettings;
-	//LLButton	*mBtnAO;
+	LLButton	*mBtnAO;
 	LLButton	*mBtnVolume;
 	LLTextBox	*mBoxBalance;
 	LLButton	*mBtnBuyL;
@@ -155,7 +155,7 @@ class LLStatusBar final
 	LLFrameTimer*	mHealthTimer;
 	LLPanelPresetsCameraPulldown* mPanelPresetsCameraPulldown;
 	LLPanelPresetsPulldown* mPanelPresetsPulldown;
-	//ALPanelAOPulldown* mPanelAOPulldown;	
+	ALPanelAOPulldown* mPanelAOPulldown;	
 	ALPanelQuickSettingsPulldown* mPanelQuickSettingsPulldown;
 	LLPanelVolumePulldown* mPanelVolumePulldown;
 	LLPanelNearByMedia*	mPanelNearByMedia;
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 6467cae5501..0475141db0a 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -31,7 +31,7 @@
 #include "llfloaterreg.h"
 #include "llviewerfloaterreg.h"
 
-//#include "alfloaterao.h"
+#include "ao.h"
 #include "alfloaterexploresounds.h"
 #include "alfloatergenerictext.h"
 #include "alfloaterlightbox.h"
@@ -552,7 +552,7 @@ void LLViewerFloaterReg::registerFloaters()
 
 	// *NOTE: Please keep these alphabetized for easier merges
 
-	//LLFloaterReg::add("ao", "floater_ao.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<ALFloaterAO>);
+	LLFloaterReg::add("ao", "floater_ao.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FloaterAO>);
     LLFloaterReg::add("asset_hex_editor", "floater_hex_editor.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHexEditor>);
 	LLFloaterReg::add("chatbar", "floater_chatbar.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLChatBar>);
 	LLFloaterReg::add("delete_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterDeleteQueue>);
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 17c662e44c4..c9d2fd9c652 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -38,7 +38,7 @@
 #include "raytrace.h"
 
 #include "alavatargroups.h"
-//#include "alaoengine.h"
+#include "aoengine.h"
 #include "llagent.h" //  Get state values from here
 #include "llagentbenefits.h"
 #include "llagentcamera.h"
@@ -3147,7 +3147,7 @@ void LLVOAvatar::idleUpdateLoadingEffect()
 					LL_INFOS("Avatar") << avString() << "self isFullyLoaded, mFirstFullyVisible" << LL_ENDL;
 					LLAppearanceMgr::instance().onFirstFullyVisible();
 
-					//ALAOEngine::instance().onLoginComplete();
+					AOEngine::instance().onLoginComplete();
 				}
 				else
 				{
@@ -3799,10 +3799,12 @@ void LLVOAvatar::idleUpdateBelowWater()
 	F32 avatar_height = (F32)(getPositionGlobal().mdV[VZ]);
 	F32 water_height = getRegion()->getWaterHeight();
 
-//	BOOL was_below_water = mBelowWater;
-	mBelowWater = avatar_height < water_height;
-//	if (isSelf() && mBelowWater != was_below_water)
-//        ALAOEngine::instance().checkBelowWater(mBelowWater);
+	BOOL wasBelowWater = mBelowWater;
+	mBelowWater =  avatar_height < water_height;
+	if (isSelf() && wasBelowWater != mBelowWater)
+	{
+		AOEngine::instance().checkBelowWater(mBelowWater);
+	}
 }
 
 void LLVOAvatar::slamPosition()
@@ -6286,12 +6288,12 @@ BOOL LLVOAvatar::startMotion(const LLUUID& id, F32 time_offset)
 #ifdef SHOW_DEBUG
 	LL_DEBUGS("Motion") << "motion requested " << id.asString() << " " << gAnimLibrary.animationName(id) << LL_ENDL;
 #endif
-#if 0
+#if 1
 	LLUUID remap_id;
-	if(isSelf())
+	if (isSelf())
 	{
-        remap_id = ALAOEngine::getInstance()->override(id, true);
-		if(remap_id.isNull())
+		remap_id = AOEngine::getInstance()->override(id, true);
+		if (remap_id.isNull())
 		{
 			remap_id = remapMotionID(id);
 		}
@@ -6336,11 +6338,11 @@ BOOL LLVOAvatar::stopMotion(const LLUUID& id, BOOL stop_immediate)
 	LL_DEBUGS("Motion") << "Motion requested " << id.asString() << " " << gAnimLibrary.animationName(id) << LL_ENDL;
 #endif
 
-#if 0
+#if 1
 	LLUUID remap_id;
-	if(isSelf())
+	if (isSelf())
 	{
-        remap_id = ALAOEngine::getInstance()->override(id, false);
+		remap_id = AOEngine::getInstance()->override(id, false);
 		if (remap_id.isNull())
 		{
 			remap_id = remapMotionID(id);
diff --git a/indra/newview/skins/default/xui/en/floater_ao.xml b/indra/newview/skins/default/xui/en/floater_ao.xml
index dca10029fd7..986c685aaf8 100644
--- a/indra/newview/skins/default/xui/en/floater_ao.xml
+++ b/indra/newview/skins/default/xui/en/floater_ao.xml
@@ -1,32 +1,46 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
- positioning="centered"
- legacy_header_height="18"
- can_resize="true"
- can_dock="false"
- can_close="true"
- height="352"
- min_height="352"
- min_width="200"
- layout="topleft"
- name="ao"
- save_rect="true"
- save_visibility="true"
- single_instance="true"
- reuse_instance="false"
- save_dock_state="false"
- title="Animation Overrider"
- width="200">
-  <floater.string name="ao_no_sets_loaded">No Sets Loaded</floater.string>
-  <floater.string name="ao_no_animations_loaded">No Animations Loaded</floater.string>
-  <panel
-   name="animation_overrider_outer_panel"
-   filename="panel_ao.xml"
-   left="0"
-   top="16"
-   width="200"
-   height="334"
-   follows="all"
-   layout="topleft">
-  </panel>
+	positioning="centered"
+	legacy_header_height="18"
+	can_resize="true"
+	can_dock="false"
+	can_close="true"
+	height="352"
+	min_height="320"
+	min_width="200"
+	layout="topleft"
+	name="ao"
+	help_topic="animation_overrider"
+	save_rect="true"
+	save_visibility="true"
+	single_instance="true"
+        reuse_instance="false"
+	save_dock_state="false"
+	title="Animation Overrider"
+	width="200">
+
+	<floater.string
+		name="ao_no_sets_loaded">
+No Sets Loaded
+	</floater.string>
+
+	<floater.string
+		name="ao_no_animations_loaded">
+No Animations Loaded
+	</floater.string>
+	<floater.string
+		name="ao_dnd_only_on_full_interface">
+You can only drag and drop an animation notecard on the full interface window. Click on the wrench icon to open it.
+	</floater.string>
+
+	<panel
+		name="animation_overrider_outer_panel"
+		filename="panel_ao.xml"
+		left="0"
+		top="16"
+		width="200"
+		height="334"
+		follows="all"
+		layout="topleft">
+	</panel>
 </floater>
diff --git a/indra/newview/skins/default/xui/en/panel_ao.xml b/indra/newview/skins/default/xui/en/panel_ao.xml
index 7f585bcad5e..3b893beac78 100644
--- a/indra/newview/skins/default/xui/en/panel_ao.xml
+++ b/indra/newview/skins/default/xui/en/panel_ao.xml
@@ -1,423 +1,490 @@
 <?xml version="1.0" encoding="utf-8"?>
 <panel
- name="animation_overrider_outer_panel"
- left="0"
- top="0"
- width="200"
- height="334"
- follows="all"
- visible="true"
- layout="topleft">
+	name="animation_overrider_outer_panel"
+	left="0"
+	top="0"
+	width="200"
+	height="324"
+	follows="all"
+	visible="true"
+	layout="topleft">
+
 <!-- Main Panel -->
-  <panel
-   name="animation_overrider_panel"
-   left="10"
-   top="4"
-   width="180"
-   height="320"
-   follows="all"
-   visible="true"
-   layout="topleft">
-    <check_box
-     name="ao_enable"
-     label="Enable AO"
-     tool_tip="Enable or disable the Animation Overrider"
-     control_name="AlchemyAOEnable"
-     left="0"
-     top="4"
-     width="20"
-     height="20"
-     follows="left|top"
-     layout="topleft" />
-    <combo_box
-     name="ao_set_selection_combo"
-     tool_tip="Select animation set to edit."
-     left="0"
-     top_pad="2"
-     right="-24"
-     height="20"
-     allow_text_entry="true"
-     max_chars="256"
-     follows="left|right|top"
-     layout="topleft">
-      <combo_box.commit_callback
-       function="AO.SelectSet" />
-    </combo_box>
-    <button
-     name="ao_activate"
-     tool_tip="Activate this animation set now."
-     left_pad="4"
-     width="20"
-     height="20"
-     follows="right|top"
-     layout="topleft">
-      <button.commit_callback
-       function="AO.ActivateSet" />
-    </button>
-    <icon
-     left_delta="4"
-     top_delta="4"
-     width="12"
-     height="12"
-     image_name="Activate_Checkmark"
-     follows="right|top"
-     layout="topleft" />
-    <check_box
-     name="ao_default"
-     label="Default"
-     tool_tip="Make this animation set the default set that plays when you log in."
-     left="0"
-     top_pad="8"
-     width="20"
-     height="20"
-     follows="left|top"
-     layout="topleft">
-      <check_box.commit_callback
-       function="AO.SetDefault" />
-    </check_box>
-    <button
-     name="ao_add"
-     tool_tip="Create a new animation set."
-     top_delta="0"
-     right="-24"
-     width="20"
-     height="20"
-     image_overlay="AddItem_Press"
-     follows="right|top"
-     layout="topleft">
-      <button.commit_callback
-       function="AO.AddSet" />
-    </button>
-    <button
-     name="ao_remove"
-     tool_tip="Remove this animation set."
-     left_pad="4"
-     width="20"
-     height="20"
-     image_overlay="TrashItem_Press"
-     follows="right|top"
-     layout="topleft">
-      <button.commit_callback
-       function="AO.RemoveSet" />
-    </button>
-    <check_box
-     name="ao_sit_override"
-     label="Override Sits"
-     tool_tip="Check this if you want sit animation overrides."
-     left="0"
-     top_pad="4"
-     width="100"
-     height="16"
-     follows="left|top"
-     layout="topleft">
-      <check_box.commit_callback
-       function="AO.SetSitOverride" />
-    </check_box>
-    <check_box
-     name="ao_smart"
-     label="Be smart"
-     tool_tip="Smart mode tries to determine if the sit override would clash with the object's own animation and disables the overrider temporarily."
-     left_pad="4"
-     right="-1"
-     height="16"
-     follows="left|top"
-     layout="topleft">
-      <check_box.commit_callback
-       function="AO.SetSmart" />
-    </check_box>
-    <check_box
-     name="ao_disable_stands_in_mouselook"
-     label="Disable Stands in Mouselook"
-     tool_tip="If you need to preserve your custom stand animation in mouselook, check this box."
-     left="0"
-     top_pad="4"
-     width="100"
-     height="16"
-     follows="left|top"
-     layout="topleft">
-      <check_box.commit_callback
-       function="AO.DisableStandsML" />
-    </check_box>
-    <combo_box
-     name="ao_state_selection_combo"
-     tool_tip="Select animation state to edit."
-     left="0"
-     top_pad="4"
-     right="-1"
-     height="20"
-     follows="left|right|top"
-     layout="topleft">
-      <combo_box.commit_callback
-       function="AO.SelectState" />
-    </combo_box>
-    <scroll_list
-     name="ao_state_animation_list"
-     top_pad="4"
-     right="-24"
-     height="98"
-     multi_select="true"
-     follows="all"
-     layout="topleft">
-      <scroll_list.columns
-       name="icon"
-       dynamic_width="false"
-       width="20" />
-      <scroll_list.columns
-       name="animation_name"
-       dynamic_width="true" />
-      <scroll_list.commit_callback
-       function="AO.SelectAnim" />
-    </scroll_list>
-    <panel
-     name="ao_animation_move_trash_panel"
-     left_pad="4"
-     right="-1"
-     height="98"
-     follows="right|top|bottom"
-     layout="topleft">
-      <button
-       name="ao_move_up"
-       tool_tip="Move the selected animation up in the list."
-       left="0"
-       top="0"
-       width="20"
-       height="32"
-       image_overlay="Arrow_Up"
-       follows="left|top"
-       layout="topleft">
-        <button.commit_callback
-         function="AO.MoveAnimUp" />
-      </button>
-      <button
-       name="ao_move_down"
-       tool_tip="Move the selected animation down in the list."
-       top_pad="4"
-       width="20"
-       height="32"
-       image_overlay="Arrow_Down"
-       follows="left|top"
-       layout="topleft">
-        <button.commit_callback
-         function="AO.MoveAnimDown" />
-      </button>
-      <button
-       name="ao_trash"
-       tool_tip="Remove the selected animation from the list."
-       left_delta="0"
-       bottom="-1"
-       width="20"
-       height="20"
-       image_overlay="TrashItem_Press"
-       follows="left|bottom"
-       layout="topleft">
-        <button.commit_callback
-         function="AO.RemoveAnim" />
-      </button>
-    </panel>
-    <check_box
-     name="ao_cycle"
-     label="Cycle"
-     tool_tip="Play a different animation from the list every time the animation state is called."
-     left="0"
-     top_pad="4"
-     width="55"
-     height="16"
-     follows="left|bottom"
-     layout="topleft">
-      <check_box.commit_callback
-       function="AO.SetCycle" />
-    </check_box>
-    <check_box
-     name="ao_randomize"
-     label="Randomize order"
-     tool_tip="Randomize order of animations in cycle mode."
-     left_pad="4"
-     width="100"
-     height="16"
-     follows="left|bottom"
-     layout="topleft">
-      <check_box.commit_callback
-       function="AO.SetRandomize" />
-    </check_box>
-    <text
-     name="ao_cycle_time_seconds_label"
-     value="Cycle time (seconds):"
-     left="0"
-     top_pad="4"
-     width="120"
-     follows="left|right|bottom"
-     layout="topleft" />
-    <spinner
-     name="ao_cycle_time"
-     tool_tip="Time before switching to the next animation in the list. Set this to 0 to disable automatic animation cycling."
-     top_delta="-6"
-     left_pad="8"
-     height="16"
-     right="-1"
-     decimal_digits="0"
-     initial_value="0"
-     min_val="0"
-     max_val="999"
-     increment="1"
-     follows="right|bottom"
-     layout="topleft">
-      <spinner.commit_callback
-       function="AO.SetCycleTime" />
-    </spinner>
-    <button
-     name="ao_reload"
-     label="Reload"
-     tool_tip="Reload animation overrider configuration."
-     left="0"
-     top_pad="8"
-     right="-1"
-     height="20"
-     follows="left|right|bottom"
-     layout="topleft">
-      <button.commit_callback
-       function="AO.Reload" />
-    </button>
-    <layout_stack
-     name="next_previous_buttons_stack"
-     left="0"
-     top_pad="4"
-     right="-1"
-     height="20"
-     orientation="horizontal"
-     follows="left|right|bottom"
-     layout="topleft">
-      <layout_panel
-       name="next_previous_buttons_stack_left"
-       width="90"
-       height="20"
-       user_resize="false"
-       follows="all"
-       layout="topleft">
-        <button
-         name="ao_previous"
-         image_overlay="BackArrow_Off"
-         tool_tip="Switch to previous animation of the current state."
-         left="0"
-         top="0"
-         width="90"
-         height="20"
-         follows="all"
-         layout="topleft">
-          <button.commit_callback
-           function="AO.PrevAnim" />
-        </button>
-      </layout_panel>
-      <layout_panel
-       name="next_previous_buttons_stack_right"
-       width="90"
-       height="20"
-       user_resize="false"
-       follows="all"
-       layout="topleft">
+
+	<panel
+		name="animation_overrider_panel"
+		left="10"
+		top="0"
+		width="180"
+		height="322"
+		follows="all"
+		visible="true"
+		layout="topleft">
+		<check_box
+		name="ao_enable"
+		label="Enable AO"
+		tool_tip="Enable or disable the Animation Overrider"
+		control_name="AlchemyAOEnable"
+		left="0"
+		top="0"
+		width="20"
+		height="20"
+		follows="left|top"
+		layout="topleft" />
+		<combo_box
+			name="ao_set_selection_combo"
+			tool_tip="Select animation set to edit."
+			left="0"
+			top_pad="2"
+			right="-24"
+			height="20"
+			allow_text_entry="true"
+			max_chars="256"
+			follows="left|right|top"
+			force_disable_fulltext_search="true"
+			layout="topleft" />
+
 		<button
-         name="ao_next"
-         image_overlay="ForwardArrow_Off"
-         tool_tip="Switch to next animation of the current state."
-         left="2"
-         top="0"
-         width="90"
-         height="20"
-         follows="all"
-         layout="topleft">
-          <button.commit_callback
-           function="AO.NextAnim" />
-        </button>
-      </layout_panel>
-    </layout_stack>
-  </panel>
-<!-- Cute Reload Cover Panel -->
-  <panel
-   name="ao_reload_cover"
-   left="0"
-   top="0"
-   width="200"
-   height="325"
-   follows="all"
-   visible="false"
-   bg_alpha_color="Black_50"
-   bg_opaque_color="Black_50"
-   background_visible="true"
-   background_opaque="true"
-   mouse_opaque="true"
-   layout="topleft">
-    <panel
-     name="ao_reload_text_panel"
-     left="30"
-     top="110"
-     right="-30"
-     bottom="-110"
-     bg_alpha_color="Black_50"
-     bg_opaque_color="Black_50"
-     background_visible="true"
-     background_opaque="true"
-     follows="left|right|top">
-      <view_border
-       name="ao_reload_view_border"
-       left="0"
-       top="0"
-       right="-1"
-       bottom="-1"
-       follows="all" />
-      <text
-       name="reload_label"
-       left_delta="0"
-       top_delta="16"
-       halign="center"
-       valign="center"
-       follows="all">
-       Reloading Config
-      </text>
-      <text
-       name="wait_label"
-       v_pad="-4"
-       bottom="-16"
-       halign="center"
-       valign="center"
-       follows="all">
-       Please Wait
-      </text>
-      <layout_stack
-       name="ao_reload_indicator_layout_stack"
-       left="0"
-       top="31"
-       right="-1"
-       height="32"
-       orientation="horizontal"
-       follows="top|left|right">
-        <layout_panel
-         name="ao_reload_indicator_left_layout_panel"
-         width="160"
-         height="32"
-         auto_resize="true"
-         user_resize="false">
-        </layout_panel>
-        <layout_panel
-         name="ao_reload_indicator_layout_panel"
-         width="32"
-         height="32"
-         auto_resize="false"
-         user_resize="false">
-        <loading_indicator
-         left="0"
-         top="0"
-         width="32"
-         height="32"
-         follows="right|top"
-         name="ao_reload_indicator" />
-        </layout_panel>
-        <layout_panel
-         name="ao_reload_indicator_right_layout_panel"
-         width="160"
-         height="32"
-         auto_resize="true"
-         user_resize="false">
-        </layout_panel>
-      </layout_stack>
-    </panel>
-  </panel>
+			name="ao_activate"
+			tool_tip="Activate this animation set now."
+			left_pad="4"
+			width="20"
+			height="20"
+			follows="right|top"
+			layout="topleft" />
+
+		<icon
+			left_delta="4"
+			top_delta="4"
+			width="12"
+			height="12"
+			image_name="Activate_Checkmark"
+			follows="right|top"
+			layout="topleft" />
+
+		<check_box
+			name="ao_default"
+			label="Default"
+			tool_tip="Make this animation set the default set that plays when you log in."
+			left="0"
+			top_pad="8"
+			width="20"
+			height="20"
+			follows="left|top"
+			layout="topleft" />
+
+		<button
+			name="ao_add"
+			tool_tip="Create a new animation set."
+			top_delta="0"
+			right="-24"
+			width="20"
+			height="20"
+			image_overlay="AddItem_Press"
+			follows="right|top"
+			layout="topleft" />
+
+		<button
+			name="ao_remove"
+			tool_tip="Remove this animation set."
+			left_pad="4"
+			width="20"
+			height="20"
+			image_overlay="TrashItem_Press"
+			follows="right|top"
+			layout="topleft" />
+
+		<check_box
+			name="ao_sit_override"
+			label="Override Sits"
+			tool_tip="Check this if you want sit animation overrides."
+			left="0"
+			top_pad="4"
+			width="100"
+			height="16"
+			follows="left|top"
+			layout="topleft" />
+
+		<check_box
+			name="ao_smart"
+			label="Be smart"
+			tool_tip="Smart mode tries to determine if the sit override would clash with the object's own animation and disables the overrider temporarily."
+			left_pad="4"
+			right="-1"
+			height="16"
+			follows="left|top"
+			layout="topleft" />
+
+		<check_box
+			name="ao_disable_stands_in_mouselook"
+			label="Disable Stands in Mouselook"
+			tool_tip="If you need to preserve your custom stand animation in mouselook, check this box."
+			left="0"
+			top_pad="4"
+			width="100"
+			height="16"
+			follows="left|top"
+			layout="topleft" />
+
+		<combo_box
+			name="ao_state_selection_combo"
+			tool_tip="Select animation state to edit."
+			left="0"
+			top_pad="4"
+			right="-1"
+			height="20"
+			follows="left|right|top"
+			layout="topleft" />
+
+		<scroll_list
+			name="ao_state_animation_list"
+			top_pad="4"
+			right="-24"
+			height="98"
+			multi_select="true"
+			follows="all"
+			layout="topleft">
+			<scroll_list.columns
+				name="icon"
+				dynamic_width="false"
+				width="20" />
+			<scroll_list.columns
+				name="animation_name"
+				dynamic_width="true" />
+		</scroll_list>
+
+		<panel
+			name="ao_animation_move_trash_panel"
+			left_pad="4"
+			right="-1"
+			height="98"
+			follows="right|top|bottom"
+			layout="topleft">
+
+			<button
+				name="ao_move_up"
+				tool_tip="Move the selected animation up in the list."
+				left="0"
+				top="0"
+				width="20"
+				height="32"
+				image_overlay="Arrow_Up"
+				follows="left|top"
+				layout="topleft" />
+
+			<button
+				name="ao_move_down"
+				tool_tip="Move the selected animation down in the list."
+				top_pad="4"
+				width="20"
+				height="32"
+				image_overlay="Arrow_Down"
+				follows="left|top"
+				layout="topleft" />
+
+			<button
+				name="ao_trash"
+				tool_tip="Remove the selected animation from the list."
+				left_delta="0"
+				bottom="-1"
+				width="20"
+				height="20"
+				image_overlay="TrashItem_Press"
+				follows="left|bottom"
+				layout="topleft" />
+		</panel>
+
+		<check_box
+			name="ao_cycle"
+			label="Cycle"
+			tool_tip="Play a different animation from the list every time the animation state is called."
+			left="0"
+			top_pad="4"
+			width="55"
+			height="16"
+			follows="left|bottom"
+			layout="topleft" />
+
+		<check_box
+			name="ao_randomize"
+			label="Randomize order"
+			tool_tip="Randomize order of animations in cycle mode."
+			left_pad="4"
+			width="100"
+			height="16"
+			follows="left|bottom"
+			layout="topleft" />
+
+		<text
+			name="ao_cycle_time_seconds_label"
+			value="Cycle time (seconds):"
+			left="0"
+			top_pad="4"
+			width="120"
+			follows="left|right|bottom"
+			layout="topleft" />
+
+		<spinner
+			name="ao_cycle_time"
+			tool_tip="Time before switching to the next animation in the list. Set this to 0 to disable automatic animation cycling."
+			top_delta="-6"
+			left_pad="8"
+			height="16"
+			right="-1"
+			decimal_digits="0"
+			initial_value="0"
+			min_val="0"
+			max_val="999"
+			increment="1"
+			follows="right|bottom"
+			layout="topleft" />
+
+		<button
+			name="ao_reload"
+			label="Reload"
+			tool_tip="Reload animation overrider configuration."
+			left="0"
+			top_pad="8"
+			right="-1"
+			height="20"
+			follows="left|right|bottom"
+			layout="topleft" />
+
+		<layout_stack
+			name="next_previous_buttons_stack"
+			left="0"
+			top_pad="4"
+			right="-24"
+			height="20"
+			orientation="horizontal"
+			follows="left|right|bottom"
+			layout="topleft">
+
+		<layout_panel
+			name="next_previous_buttons_stack_left"
+			width="90"
+			height="20"
+			user_resize="false"
+			follows="all"
+			layout="topleft">
+
+		<button
+			name="ao_previous"
+			image_overlay="BackArrow_Off"
+			tool_tip="Switch to previous animation of the current state."
+			left="0"
+			top="0"
+			width="88"
+			height="20"
+			follows="all"
+			layout="topleft" />
+
+		</layout_panel>
+		<layout_panel
+			name="next_previous_buttons_stack_right"
+			width="90"
+			height="20"
+			user_resize="false"
+			follows="all"
+			layout="topleft">
+
+		<button
+			name="ao_next"
+			image_overlay="ForwardArrow_Off"
+			tool_tip="Switch to next animation of the current state."
+			left="2"
+			top="0"
+			width="88"
+			height="20"
+			follows="all"
+			layout="topleft" />
+
+		</layout_panel>
+		</layout_stack>
+
+		<button
+			name="ao_less"
+			tool_tip="Show small Animation Overrider interface."
+			image_overlay="DisclosureArrow_Opened_Off"
+			left_pad="4"
+			width="20"
+			height="20"
+			follows="right|bottom"
+			layout="topleft" />
+
+	</panel>
+
+<!-- Reload Cover Panel -->
+
+	<panel
+		name="ao_reload_cover"
+		left="0"
+		top="0"
+		width="200"
+		height="315"
+		follows="all"
+		visible="false"
+		bg_alpha_color="Black_50"
+		bg_opaque_color="Black_50"
+		background_visible="true"
+		background_opaque="true"
+		mouse_opaque="true"
+		layout="topleft">
+
+		<panel
+			name="ao_reload_text_panel"
+			left="30"
+			top="110"
+			right="-30"
+			bottom="-110"
+			bg_alpha_color="Black_50"
+			bg_opaque_color="Black_50"
+			background_visible="true"
+			background_opaque="true"
+			follows="left|right|top">
+
+			<view_border
+				name="ao_reload_view_border"
+				left="0"
+				top="0"
+				right="-1"
+				bottom="-1"
+				follows="all" />
+			<text
+				name="reload_label"
+				left_delta="0"
+				top_delta="16"
+				halign="center"
+				valign="center"
+				follows="all">
+				Reloading Config
+			</text>
+			<text
+				name="wait_label"
+				v_pad="-4"
+				bottom="-16"
+				halign="center"
+				valign="center"
+				follows="all">
+				Please Wait
+			</text>
+
+			<layout_stack
+				name="ao_reload_indicator_layout_stack"
+				left="0"
+				top="31"
+				right="-1"
+				height="32"
+				orientation="horizontal"
+				follows="top|left|right">
+			<layout_panel
+				name="ao_reload_indicator_left_layout_panel"
+				width="160"
+				height="32"
+				auto_resize="true"
+				user_resize="false">
+			</layout_panel>
+			<layout_panel
+				name="ao_reload_indicator_layout_panel"
+				width="32"
+				height="32"
+				auto_resize="false"
+				user_resize="false">
+			<loading_indicator
+				left="0"
+				top="0"
+				width="32"
+				height="32"
+				follows="right|top"
+				name="ao_reload_indicator" />
+			</layout_panel>
+			<layout_panel
+				name="ao_reload_indicator_right_layout_panel"
+				width="160"
+				height="32"
+				auto_resize="true"
+				user_resize="false">
+			</layout_panel>
+			</layout_stack>
+
+		</panel>
+	</panel>
+
+<!-- Small Panel -->
+
+	<panel
+		name="animation_overrider_panel_small"
+		left="10"
+		top="6"
+		width="180"
+		height="65"
+		visible="false"
+		follows="left|right|top"
+		layout="topleft">
+
+		<check_box
+		name="ao_enable"
+		label="Enable AO"
+		tool_tip="Enable or disable the Animation Overrider"
+		control_name="AlchemyAOEnable"
+		left="0"
+		top="0"
+		width="20"
+		height="10"
+		follows="left|top"
+		layout="topleft" />
+
+	<combo_box
+		name="ao_set_selection_combo_small"
+		tool_tip="Select animation set to edit."
+		left="0"
+		top_pad="3"
+		right="-24"
+		height="20"
+		follows="left|right|top"
+		force_disable_fulltext_search="true"
+		layout="topleft" />
+
+	<button
+		name="ao_more"
+		tool_tip="Show full interface for configuration."
+		image_overlay="Edit_Wrench"
+		left_pad="4"
+		right="-1"
+		height="20"
+		follows="right|top"
+		layout="topleft" />
+
+	<button
+		name="ao_previous_small"
+		image_overlay="BackArrow_Off"
+		tool_tip="Switch to previous animation of the current state."
+		left="0"
+		top_pad="4"
+		width="62"
+		height="20"
+		follows="left|top"
+		layout="topleft" />
+
+	<button
+		name="ao_next_small"
+		image_overlay="ForwardArrow_Off"
+		tool_tip="Switch to next animation of the current state."
+		left_pad="4"
+		width="62"
+		height="20"
+		follows="left|top"
+		layout="topleft" />
+
+	<check_box
+		name="ao_sit_override_small"
+		label="Sits"
+		tool_tip="Check this if you want sit animation overrides."
+		left_pad="4"
+		width="45"
+		height="16"
+		follows="right|top"
+		layout="topleft" />
+
+	</panel>
 </panel>
diff --git a/indra/newview/skins/default/xui/en/panel_status_bar.xml b/indra/newview/skins/default/xui/en/panel_status_bar.xml
index 256d03a1a90..fc16ba2c886 100644
--- a/indra/newview/skins/default/xui/en/panel_status_bar.xml
+++ b/indra/newview/skins/default/xui/en/panel_status_bar.xml
@@ -162,7 +162,16 @@
      name="presets_icon_graphic"
 	 tool_tip="Graphics Presets"
      width="16" />
-
+    <button
+     follows="right|top"
+     height="16"
+     image_selected="Move"
+     image_unselected="Move_Off"
+     is_toggle="true"
+     left_pad="5"
+     top="2"
+     name="ao_btn"
+     width="16" />
     <button
      follows="right|top"
      height="16"
-- 
GitLab