Split up floaterao.* into aosystem.* and aostate.h, also further refactor

This commit is contained in:
Liru Færs
2020-03-22 15:02:08 -04:00
parent a3d2107bfd
commit 0f6285b2e9
11 changed files with 759 additions and 770 deletions

View File

@@ -92,6 +92,7 @@ set(viewer_SOURCE_FILES
aixmllindengenepool.cpp
alfloaterregiontracker.cpp
aoremotectrl.cpp
aosystem.cpp
ascentfloatercontactgroups.cpp
ascentkeyword.cpp
ascentprefschat.cpp
@@ -636,6 +637,8 @@ set(viewer_HEADER_FILES
aixmllindengenepool.h
alfloaterregiontracker.h
aoremotectrl.h
aostate.h
aosystem.h
ascentfloatercontactgroups.h
ascentkeyword.h
ascentprefschat.h

37
indra/newview/aostate.h Normal file
View File

@@ -0,0 +1,37 @@
#pragma once
enum AOState : U8
{
STATE_AGENT_IDLE,
STATE_AGENT_WALK,
STATE_AGENT_RUN,
STATE_AGENT_PRE_JUMP,
STATE_AGENT_JUMP,
STATE_AGENT_TURNLEFT,
STATE_AGENT_TURNRIGHT,
STATE_AGENT_SIT,
STATE_AGENT_SIT_GROUND,
STATE_AGENT_HOVER,
STATE_AGENT_HOVER_DOWN,
STATE_AGENT_HOVER_UP,
STATE_AGENT_CROUCH,
STATE_AGENT_CROUCHWALK,
STATE_AGENT_FALLDOWN,
STATE_AGENT_STANDUP,
STATE_AGENT_LAND,
STATE_AGENT_FLY,
STATE_AGENT_FLYSLOW,
STATE_AGENT_TYPE,
STATE_AGENT_SWIM_DOWN,
STATE_AGENT_SWIM_UP,
STATE_AGENT_SWIM,
STATE_AGENT_FLOAT,
STATE_AGENT_END
};

596
indra/newview/aosystem.cpp Normal file
View File

@@ -0,0 +1,596 @@
/**
* @file aosystem.cpp
* @brief clientside animation overrider
* by Skills Hak & Liru Færs
*/
#include "llviewerprecompiledheaders.h"
#include "aosystem.h"
#include "floaterao.h"
#include "llagent.h"
#include "llagentcamera.h"
#include "llanimationstates.h"
#include "llinventoryfunctions.h"
#include "llinventorypanel.h"
#include "llinventorymodelbackgroundfetch.h"
#include "llmemorystream.h"
#include "llnotecard.h"
#include "llstartup.h"
#include "llvoavatarself.h"
#include "llviewerregion.h"
#include "roles_constants.h"
#include <boost/regex.hpp>
// Uncomment and use instead if we ever add the chatbar as a command line - MC
void cmdline_printchat(const std::string& message);
namespace
{
bool sSwimming = false;
bool enable_swim()
{
static const LLCachedControl<bool> swim(gSavedSettings, "AOSwimEnabled", false);
return swim;
}
bool is_underwater() { return enable_swim() && gAgentAvatarp && gAgentAvatarp->mBelowWater; }
}
// -------------------------------------------------------
void AOSystem::AOStandTimer::reset()
{
mPeriod = gSavedSettings.getF32("AOStandInterval");
mEventTimer.reset();
// LL_INFOS() << "reset" << LL_ENDL;
}
// -------------------------------------------------------
class AOInvTimer final : public LLEventTimer, public LLSingleton<AOInvTimer>
{
friend class LLSingleton<AOInvTimer>;
friend class AOSystem;
AOInvTimer() : LLEventTimer(1.0f) {}
~AOInvTimer() {}
static void createIfNeeded()
{
if (needed()) LLSingleton::getInstance();
else AOSystem::getInstance();
}
public:
static bool needed()
{
return LLStartUp::getStartupState() < STATE_INVENTORY_SEND // Haven't done inventory transfer yet
|| !LLInventoryModelBackgroundFetch::instance().isEverythingFetched(); // Everything hasn't been fetched yet
}
BOOL tick() override
{
if (!gSavedSettings.getBOOL("AOEnabled")) return true; // If disabled on a tick, we don't care anymore
if (!needed())
{
// cmdline_printchat("Inventory fetched, loading AO.");
AOSystem::getInstance(); // Initializes everything
return true;
}
return false;
}
};
class ObjectNameMatches final : public LLInventoryCollectFunctor
{
public:
ObjectNameMatches(const std::string& name, const LLUUID& folder) : mName(name), mFolder(folder) {}
virtual ~ObjectNameMatches() {}
bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) override
{
return item && item->getParentUUID() == mFolder && item->getName() == mName;
}
private:
const std::string& mName;
const LLUUID& mFolder;
};
static LLUUID invfolderid;
LLUUID getAssetIDByName(const std::string& name)
{
if (name.empty()) return LLUUID::null;
LLViewerInventoryCategory::cat_array_t cats;
LLViewerInventoryItem::item_array_t items;
ObjectNameMatches matches(name, invfolderid);
gInventory.collectDescendentsIf(LLUUID::null, cats, items, false, matches, true);
return items.empty() ? LLUUID(name) : items[0]->getAssetUUID();
};
void AOSystem::start()
{
llassert(!instanceExists() && !AOInvTimer::instanceExists()); // This should only be run once!
auto control(gSavedSettings.getControl("AOEnabled"));
if (control->get()) AOInvTimer::createIfNeeded(); // Start the clock
control->getSignal()->connect([](LLControlVariable*, const LLSD& enabled) {
if (enabled.asBoolean()) AOInvTimer::createIfNeeded(); // Start the clock
else deleteSingleton();
});
}
// STUFF -------------------------------------------------------
AOSystem::overrides::overrides(const char* setting_name)
: setting(setting_name ? gSavedPerAccountSettings.getControl("AODefault" + std::string(setting_name)) : nullptr)
, playing(false)
{
}
bool AOSystem::overrides::play_condition() const
{
// Stop stand first then play
instance().stopCurrentStand();
return false;
}
void AOSystem::overrides::play(bool start)
{
if (playing == start) return;
// LL_INFOS() << "st" << (start ? "art" : "op") << " override: " << aop->getOverride() << LL_ENDL;
// We can always stop, but starting is particular
if (start)
{
if (play_condition())
return;
}
else if (playing && !isLowPriority()) // Stands were turned off if this isn't a low priority overrides
{
if (!isAgentAvatarValid() && !gAgentAvatarp->isSitting() && !gAgent.getFlying()) // check if sitting or hovering
instance().stand().play();
}
playing = start;
gAgent.sendAnimationRequest(ao_id, start ? ANIM_REQUEST_START : ANIM_REQUEST_STOP);
}
struct override_low_priority final : public AOSystem::overrides
{
override_low_priority(const LLUUID& id, const char* setting_name = nullptr)
: overrides(setting_name), orig_id(id) {}
bool overrideAnim(bool swimming, const LLUUID& anim) const override { return orig_id == anim; }
bool play_condition() const override { return false; }
bool isLowPriority() const override { return true; }
private:
const LLUUID orig_id;
};
struct override_single final : public AOSystem::overrides
{
override_single(const LLUUID& id, const char* setting_name = nullptr, U8 swim = 2)
: overrides(setting_name), orig_id(id), swim(swim) {}
bool overrideAnim(bool swimming, const LLUUID& anim) const override { return (swim == 2 || !!swim == swimming) && orig_id == anim; }
private:
const LLUUID orig_id;
const U8 swim; // 2 = irrelevant, 0 = flying, 1 = swimming
};
struct override_sit final : public AOSystem::overrides
{
override_sit(const uuid_set_t& ids, const char* setting_name = nullptr)
: overrides(setting_name)
, orig_ids(ids)
{}
bool overrideAnim(bool swimming, const LLUUID& anim) const override { return orig_ids.find(anim) != orig_ids.end(); }
bool play_condition() const override
{
overrides::play_condition();
return !gSavedSettings.getBOOL("AOSitsEnabled");
}
private:
const uuid_set_t orig_ids;
};
bool AOSystem::override_stand::play_condition() const
{
// Do not call base, stands do their own thing
// stands have lowest priority
return (!isAgentAvatarValid() || gAgentAvatarp->isSitting())
&& (gAgentCamera.cameraMouselook() && gSavedSettings.getBOOL("AONoStandsInMouselook"));
}
bool AOSystem::override_stand::overrideAnim(bool swimming, const LLUUID& anim) const
{
return anim == ANIM_AGENT_STAND;
}
AOSystem::AOSystem()
: stand_iterator(0)
, mAOOverrides({})
{
// TODO: When we move to C++20 and when GCC and MSVC support it (at the time of writing, neither fully do), use __VA_OPT__ here instead.
#define ANY_OVERRIDE(which, state, ...) mAOOverrides[STATE_AGENT_##state] = new which(ANIM_AGENT_##state, ##__VA_ARGS__)
#define BASIC_OVERRIDE(state, ...) ANY_OVERRIDE(override_single, ##state, ##__VA_ARGS__)
mAOOverrides[STATE_AGENT_IDLE] = new override_stand();
BASIC_OVERRIDE(WALK, "Walk");
BASIC_OVERRIDE(RUN, "Run");
BASIC_OVERRIDE(PRE_JUMP, "PreJump");
BASIC_OVERRIDE(JUMP, "Jump");
BASIC_OVERRIDE(TURNLEFT);
BASIC_OVERRIDE(TURNRIGHT);
#define SIT_OVERRIDE(control, state, ...) mAOOverrides[STATE_AGENT_##state] = new override_sit(uuid_set_t{ANIM_AGENT_##state, __VA_ARGS__}, control)
SIT_OVERRIDE("Sit", SIT, ANIM_AGENT_SIT_FEMALE, ANIM_AGENT_SIT_GENERIC);
SIT_OVERRIDE("GroundSit", SIT_GROUND, ANIM_AGENT_SIT_GROUND_CONSTRAINED);
#undef SIT_OVERRIDE
BASIC_OVERRIDE(HOVER, "Hover", false);
BASIC_OVERRIDE(HOVER_DOWN, "FlyDown", false);
BASIC_OVERRIDE(HOVER_UP, "FlyUp", false);
BASIC_OVERRIDE(CROUCH, "Crouch");
BASIC_OVERRIDE(CROUCHWALK, "CrouchWalk");
BASIC_OVERRIDE(FALLDOWN, "Fall");
BASIC_OVERRIDE(STANDUP, "StandUp");
BASIC_OVERRIDE(LAND, "Land");
BASIC_OVERRIDE(FLY, "Fly", false);
BASIC_OVERRIDE(FLYSLOW, "FlySlow", false);
ANY_OVERRIDE(override_low_priority, TYPE, "Typing");
mAOOverrides[STATE_AGENT_SWIM_DOWN] = new override_single(ANIM_AGENT_HOVER_DOWN, "SwimDown", true);
mAOOverrides[STATE_AGENT_SWIM_UP] = new override_single(ANIM_AGENT_HOVER_UP, "SwimUp", true);
mAOOverrides[STATE_AGENT_SWIM] = new override_single(ANIM_AGENT_FLY, "Swim", true);
mAOOverrides[STATE_AGENT_FLOAT] = new override_single(ANIM_AGENT_HOVER, "Float", true);
#undef BASIC_OVERRIDE
#undef ANY_OVERRIDE
auto swim_forced = gSavedSettings.getControl("AOSwimForced");
sSwimming = swim_forced->get().asBoolean() || is_underwater();
mConnections[0] = gSavedSettings.getControl("AOSitsEnabled")->getSignal()->connect([this](LLControlVariable*, const LLSD& val) {
if (!isAgentAvatarValid() || !gAgentAvatarp->isSitting()) return;
gAgent.sendAnimationRequest(mAOOverrides[gAgentAvatarp->getParent() ? STATE_AGENT_SIT : STATE_AGENT_SIT_GROUND]->ao_id, val.asBoolean() ? ANIM_REQUEST_START : ANIM_REQUEST_STOP);
});
const auto& swim_cb = [=](LLControlVariable*, const LLSD&){ toggleSwim(swim_forced->get().asBoolean() || is_underwater()); };
auto swim_enabled = gSavedSettings.getControl("AOSwimEnabled");
mConnections[1] = swim_enabled->getSignal()->connect(swim_cb);
mConnections[2] = swim_forced->getSignal()->connect([swim_cb, swim_enabled](LLControlVariable*, const LLSD& val) {
if (val.asBoolean()) // Automatically enable Swim AO.
swim_enabled->set(true);
swim_cb(nullptr, LLSD());
});
}
AOSystem::~AOSystem()
{
stopAllOverrides();
for (auto& ao : mAOOverrides)
{
if (ao)
{
delete ao;
ao = nullptr;
}
}
}
void AOSystem::requestConfigNotecard(bool reload)
{
LLUUID configncitem = (LLUUID)gSavedPerAccountSettings.getString("AOConfigNotecardID");
if (configncitem.notNull())
{
bool success = false;
if (const LLInventoryItem* item = gInventory.getItem(configncitem))
{
if (gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE) || gAgent.isGodlike())
{
if (item->getAssetUUID().notNull())
{
invfolderid = item->getParentUUID();
gAssetStorage->getInvItemAsset(LLHost::invalid,
gAgentID,
gAgentSessionID,
item->getPermissions().getOwner(),
LLUUID::null,
item->getUUID(),
item->getAssetUUID(),
item->getType(),
[reload](LLVFS* vfs, const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status)
{
parseNotecard(vfs, asset_uuid, type, status, reload);
},
nullptr,
true);
success = true;
}
}
}
if (!success) cmdline_printchat("Could not read the specified Config Notecard");
}
}
void AOSystem::initSingleton()
{
mAOStands.clear();
requestConfigNotecard();
}
void AOSystem::typing(bool start)
{
uuid_vec_t anims;
// If we're stopping, stop regardless, just in case the setting was toggled during (e.g.: keyboard shortcut)
if (!start || gSavedSettings.getBOOL("PlayTypingAnim")) // Linden typing
anims.push_back(ANIM_AGENT_TYPE);
if (const auto& ao = getIfExists()) // Typing override
anims.push_back(ao->mAOOverrides[STATE_AGENT_TYPE]->getOverride());
if (anims.empty()) return;
gAgent.sendAnimationRequests(anims, start ? ANIM_REQUEST_START : ANIM_REQUEST_STOP);
}
AOState GetStateFromToken(const std::string& strtoken)
{
if (strtoken == "[ Sitting On Ground ]") return STATE_AGENT_SIT_GROUND;
if (strtoken == "[ Sitting ]") return STATE_AGENT_SIT;
if (strtoken == "[ Crouching ]") return STATE_AGENT_CROUCH;
if (strtoken == "[ Crouch Walking ]") return STATE_AGENT_CROUCHWALK;
if (strtoken == "[ Standing Up ]") return STATE_AGENT_STANDUP;
if (strtoken == "[ Falling ]") return STATE_AGENT_FALLDOWN;
if (strtoken == "[ Flying Down ]") return STATE_AGENT_HOVER_DOWN;
if (strtoken == "[ Flying Up ]") return STATE_AGENT_HOVER_UP;
if (strtoken == "[ Flying Slow ]") return STATE_AGENT_FLYSLOW;
if (strtoken == "[ Flying ]") return STATE_AGENT_FLY;
if (strtoken == "[ Hovering ]") return STATE_AGENT_HOVER;
if (strtoken == "[ Jumping ]") return STATE_AGENT_JUMP;
if (strtoken == "[ Pre Jumping ]") return STATE_AGENT_PRE_JUMP;
if (strtoken == "[ Running ]") return STATE_AGENT_RUN;
if (strtoken == "[ Turning Right ]") return STATE_AGENT_TURNRIGHT;
if (strtoken == "[ Turning Left ]") return STATE_AGENT_TURNLEFT;
if (strtoken == "[ Walking ]") return STATE_AGENT_WALK;
if (strtoken == "[ Landing ]") return STATE_AGENT_LAND;
if (strtoken == "[ Standing ]") return STATE_AGENT_IDLE;
if (strtoken == "[ Swimming Down ]") return STATE_AGENT_SWIM_DOWN;
if (strtoken == "[ Swimming Up ]") return STATE_AGENT_SWIM_UP;
if (strtoken == "[ Swimming Forward ]") return STATE_AGENT_SWIM;
if (strtoken == "[ Floating ]") return STATE_AGENT_FLOAT;
if (strtoken == "[ Typing ]") return STATE_AGENT_TYPE;
return STATE_AGENT_END;
}
void AOSystem::updateStand()
{
auto& stand_ao = stand();
bool is_standing = stand_ao.playing;
if (is_standing) stand_ao.play(false); // Stop stand first
stand_ao.update(mAOStands, stand_iterator); // Now update stand
if (is_standing && !mAOStands.empty()) // Play stand if needed
stand_ao.play();
}
int AOSystem::cycleStand(bool next, bool random)
{
if (mAOStands.empty()) return -1;
const int size = mAOStands.size();
if (size > 1)
{
if (random && gSavedSettings.getBOOL("AOStandRandomize"))
for (auto previous = stand_iterator; previous == stand_iterator;
stand_iterator = ll_rand(size));
else
{
stand_iterator += next ? 1 : -1;
// Wrap around
if (stand_iterator == size) stand_iterator = 0;
else if (stand_iterator == -1) stand_iterator = size - 1;
}
if (auto floater = LLFloaterAO::findInstance())
if (auto combo = floater->getComboFromState(STATE_AGENT_IDLE))
combo->selectNthItem(stand_iterator);
// LL_INFOS() << "changing stand to " << mAOStands[stand_iterator].anim_name << LL_ENDL;
}
updateStand();
return stand_iterator;
}
void AOSystem::toggleSwim(bool underwater)
{
sSwimming = underwater && enable_swim();
if (isStanding()) return; // Don't bother if we're just standing (Who pushed us?!)
typedef std::array<overrides*, 4> flies_t;
// Stop all of the previous states
#define AOO(state) mAOOverrides[STATE_AGENT_##state]
const flies_t swims = { AOO(FLOAT), AOO(SWIM), AOO(SWIM_UP), AOO(SWIM_DOWN) };
const flies_t flies = { AOO(HOVER), AOO(FLY), AOO(HOVER_UP), AOO(HOVER_DOWN) };
#undef AOO
const auto& oldaos = sSwimming ? flies : swims;
const auto& newaos = sSwimming ? swims : flies;
for (auto i = 0; i < oldaos.size(); ++i)
{
auto& old = *oldaos[i];
if (old.playing)
{
old.play(false);
newaos[i]->play();
}
}
}
void AOSystem::doMotion(const LLUUID& id, bool start)
{
if (id.isNull() || !gAgentAvatarp) return;
overrides* aop = nullptr;
AOState state = STATE_AGENT_IDLE;
for (U8 i = STATE_AGENT_IDLE; !aop && i < STATE_AGENT_END; ++i)
{
auto& ao = mAOOverrides[i];
if (ao && ao->overrideAnim(sSwimming, id))
{
aop = ao;
state = (AOState)i;
}
}
if (aop)
{
// LL_INFOS() << "st" << (start ? "art" : "op") << " anim " << id << " state " << state << LL_ENDL;
aop->play(start);
}
}
void AOSystem::stopAllOverrides() const
{
if (!isAgentAvatarValid()) return;
uuid_vec_t anims;
for (auto& ao : mAOOverrides)
{
if (ao->playing)
{
anims.push_back(ao->getOverride());
ao->playing = false;
}
}
for (const auto& stand : mAOStands)
anims.push_back(stand.ao_id);
gAgent.sendAnimationRequests(anims, ANIM_REQUEST_STOP);
}
void AOSystem::parseNotecard(LLVFS *vfs, const LLUUID& asset_uuid, LLAssetType::EType type, S32 status, bool reload)
{
if (status == LL_ERR_NOERR)
{
if (type == LLAssetType::AT_NOTECARD)
{
AOSystem* self = getIfExists();
S32 size = vfs->getSize(asset_uuid, LLAssetType::AT_NOTECARD);
U8* buffer = new U8[size];
vfs->getData(asset_uuid, type, buffer, 0, size);
LLMemoryStream str(buffer, size);
LLNotecard nc;
if (nc.importStream(str))
{
LL_INFOS() << "ao nc decode success" << LL_ENDL;
if (self && reload) self->stopAllOverrides();
auto floater = LLFloaterAO::findInstance();
if (floater)
for (auto& combo : floater->mCombos)
{
if (combo)
{
combo->clear();
combo->removeall();
}
}
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
boost::char_separator<char> sep("\n");
tokenizer tokline(nc.getText(), sep);
for (const auto& strline : tokline)
{
// LL_INFOS() << "uncommented line: " << strline << LL_ENDL;
boost::regex type("^(\\s*)(\\[ )(.*)( \\])");
boost::smatch what;
if (boost::regex_search(strline, what, type))
{
const std::string strtoken(what[0]);
const AOState state = GetStateFromToken(strtoken);
if (state == STATE_AGENT_END)
{
LL_WARNS() << "Invalid token: " << strtoken << LL_ENDL;
continue;
}
// LL_INFOS() << "type: " << strtoken << LL_ENDL;
LLComboBox* combo = floater ? floater->getComboFromState(state) : nullptr;
auto aop = (reload && self) ? self->mAOOverrides[state] : nullptr;
// LL_INFOS() << "anims in type: " << boost::regex_replace(strline, type, "") << LL_ENDL;
boost::char_separator<char> sep("|,");
std::string stranimnames(boost::regex_replace(strline, type, ""));
tokenizer tokanimnames(stranimnames, sep);
for (const auto& stranim : tokanimnames)
{
if (stranim.empty()) continue;
const auto& animid(getAssetIDByName(stranim));
// LL_INFOS() << invfolderid.asString().c_str() << LL_ENDL;
// LL_INFOS() << "anim: " << stranim.c_str() << " assetid: " << animid << LL_ENDL;
if (animid.notNull())
{
if (aop) // If we're reloading
{
if (state == STATE_AGENT_IDLE)
self->mAOStands.push_back({ animid, stranim });
else
aop->ao_id = animid;
}
if (combo && !combo->selectByValue(stranim)) // check if exists
combo->add(stranim, ADD_BOTTOM, true);
}
else cmdline_printchat("Warning: animation '" + stranim + "' could not be found (Section: " + strtoken + ").");
}
}
}
LL_INFOS() << "ao nc read sucess" << LL_ENDL;
if (self)
{
if (auto combo = floater ? floater->getComboFromState(STATE_AGENT_IDLE) : nullptr)
combo->selectNthItem(self->stand_iterator);
for (U8 i = STATE_AGENT_IDLE+1; i < STATE_AGENT_END; ++i)
{
auto& aop = self->mAOOverrides[i];
if (!aop) continue;
auto& ao = *aop;
const auto& setting = ao.setting;
if (!setting && ao.ao_id.isNull()) continue;
const auto& defaultanim = setting ? setting->get().asStringRef() : ao.ao_id.asString();
if (defaultanim.empty()) continue;
const LLUUID& ao_id = getAssetIDByName(defaultanim);
if (reload && ao_id.notNull()) ao.ao_id = ao_id;
if (LLComboBox* combo = floater ? floater->getComboFromState(i) : nullptr)
if (!combo->selectByValue(defaultanim))
combo->add(defaultanim, ADD_BOTTOM, true);
}
if (reload && isAgentAvatarValid())
{
// Fix the stand iter, container size may have changed
auto& iter = self->stand_iterator;
const auto& stands = self->mAOStands;
iter = llmin(iter, (int)stands.size()-1);
// Update the current stand
self->stand().update(stands, iter);
self->mAOStandTimer.reset();
const auto& anims = gAgentAvatarp->mPlayingAnimations;
bool playing = false;
for (auto& aop : self->mAOOverrides)
{
for (const auto& anim : anims)
{
if (aop && aop->overrideAnim(sSwimming, anim.first))
{
if (!aop->isLowPriority()) playing = true;
aop->play();
break;
}
}
}
if (!playing) self->stand().play(); // Play stand if nothing was being played, this sometimes happens
// Toggle typing AO the moment we toggle AO
typing(gAgent.getRenderState() & AGENT_STATE_TYPING);
}
}
}
else LL_INFOS() << "ao nc decode error" << LL_ENDL;
delete[] buffer;
}
}
else LL_INFOS() << "ao nc read error" << LL_ENDL;
}

101
indra/newview/aosystem.h Normal file
View File

@@ -0,0 +1,101 @@
#pragma once
#include "aostate.h"
#include "llcontrol.h"
#include "lleventtimer.h"
class AOSystem final : public LLSingleton<AOSystem>
{
friend class LLSingleton<AOSystem>;
friend class AOInvTimer;
friend class LLFloaterAO;
AOSystem();
~AOSystem();
public:
static void start(); // Runs the necessary actions to get the AOSystem ready, then initializes it.
void initSingleton() override;
static void typing(bool start);
int stand_iterator;
bool isStanding() const { return stand().playing; }
void updateStand();
int cycleStand(bool next = true, bool random = true);
void toggleSwim(bool underwater);
void doMotion(const LLUUID& id, bool start);
void startMotion(const LLUUID& id) { doMotion(id, true); }
void stopMotion(const LLUUID& id) { doMotion(id, false); }
void stopCurrentStand() const { stand().play(false); }
void stopAllOverrides() const;
protected:
struct struct_stands
{
LLUUID ao_id;
std::string anim_name;
};
typedef std::vector<struct_stands> stands_vec;
stands_vec mAOStands;
struct overrides
{
virtual bool overrideAnim(bool swimming, const LLUUID& anim) const = 0;
virtual const LLUUID& getOverride() const { return ao_id; }
virtual bool play_condition() const; // True if prevented from playing
virtual bool isLowPriority() const { return false; }
void play(bool start = true);
LLUUID ao_id;
LLPointer<LLControlVariable> setting;
bool playing;
virtual ~overrides() {}
protected:
overrides(const char* setting_name);
};
friend struct override_low_priority;
friend struct override_single;
friend struct override_sit;
struct override_stand final : public overrides
{
override_stand() : overrides(nullptr) {}
bool overrideAnim(bool swimming, const LLUUID& anim) const override;
bool play_condition() const override;
bool isLowPriority() const override { return true; }
void update(const stands_vec& stands, const int& iter)
{
if (stands.empty()) ao_id.setNull();
else ao_id = stands[iter].ao_id;
}
};
std::array<overrides*, STATE_AGENT_END> mAOOverrides;
override_stand& stand() const { return static_cast<override_stand&>(*mAOOverrides[STATE_AGENT_IDLE]); }
private:
std::array<boost::signals2::scoped_connection, 3> mConnections;
class AOStandTimer final : public LLEventTimer
{
friend class AOSystem;
public:
AOStandTimer() : LLEventTimer(F32_MAX) {}
~AOStandTimer()
{
// LL_INFOS() << "dead" << LL_ENDL;
}
BOOL tick() override
{
// LL_INFOS() << "tick" << LL_ENDL;
if (auto ao = AOSystem::getIfExists())
ao->cycleStand();
return false;
}
void reset();
};
AOStandTimer mAOStandTimer;
static void requestConfigNotecard(bool reload = true);
static void parseNotecard(LLVFS* vfs, const LLUUID& asset_uuid, LLAssetType::EType type, S32 status, bool reload);
};

View File

@@ -1,49 +1,27 @@
/**
* @file llfloaterao.cpp
* @brief clientside animation overrider
* @brief clientside animation overrider floater
* by Skills Hak & Liru F<>rs
*/
#include "llviewerprecompiledheaders.h"
#include "floaterao.h"
#include "aosystem.h"
#include "llagent.h"
#include "llagentcamera.h"
#include "llanimationstates.h"
#include "llcombobox.h"
#include "llfirstuse.h"
#include "llinventory.h"
#include "llinventorybridge.h"
#include "llinventoryfunctions.h"
#include "llinventorypanel.h"
#include "llinventorymodelbackgroundfetch.h"
#include "llmemorystream.h"
#include "llnotecard.h"
#include "llpreviewnotecard.h"
#include "llstartup.h"
#include "lltexteditor.h"
#include "lluictrlfactory.h"
#include "llvoavatarself.h"
#include "llviewerregion.h"
#include "roles_constants.h"
#include <boost/regex.hpp>
// Uncomment and use instead if we ever add the chatbar as a command line - MC
void cmdline_printchat(const std::string& message);
namespace
{
bool sSwimming = false;
bool enable_swim()
{
static const LLCachedControl<bool> swim(gSavedSettings, "AOSwimEnabled", false);
return swim;
}
bool is_underwater() { return enable_swim() && gAgentAvatarp && gAgentAvatarp->mBelowWater; }
}
class AONotecardCallback final : public LLInventoryCallback
{
public:
@@ -53,7 +31,7 @@ public:
{
if (!mFileName.empty())
{
LLPreviewNotecard* nc = (LLPreviewNotecard*)LLPreview::show(inv_item);
auto nc = (LLPreviewNotecard*)LLPreview::show(inv_item);
if (!nc)
{
auto item = gInventory.getItem(inv_item);
@@ -89,101 +67,6 @@ private:
};
// -------------------------------------------------------
AOStandTimer::AOStandTimer() : LLEventTimer(F32_MAX)
{
}
AOStandTimer::~AOStandTimer()
{
// LL_INFOS() << "dead" << LL_ENDL;
}
void AOStandTimer::reset()
{
mPeriod = gSavedSettings.getF32("AOStandInterval");
mEventTimer.reset();
// LL_INFOS() << "reset" << LL_ENDL;
}
BOOL AOStandTimer::tick()
{
// LL_INFOS() << "tick" << LL_ENDL;
if (auto ao = AOSystem::getIfExists())
ao->cycleStand();
return false;
}
// -------------------------------------------------------
AOInvTimer::AOInvTimer() : LLEventTimer( 1.0f )
{
}
AOInvTimer::~AOInvTimer()
{
}
bool AOInvTimer::needed()
{
return LLStartUp::getStartupState() < STATE_INVENTORY_SEND // Haven't done inventory transfer yet
|| !LLInventoryModelBackgroundFetch::instance().isEverythingFetched(); // Everything hasn't been fetched yet
}
void AOInvTimer::createIfNeeded()
{
if (needed()) LLSingleton::getInstance();
else AOSystem::getInstance();
}
BOOL AOInvTimer::tick()
{
if (!gSavedSettings.getBOOL("AOEnabled")) return true; // If disabled on a tick, we don't care anymore
if (!needed())
{
// cmdline_printchat("Inventory fetched, loading AO.");
AOSystem::getInstance(); // Initializes everything
return true;
}
return false;
}
class ObjectNameMatches final : public LLInventoryCollectFunctor
{
public:
ObjectNameMatches(const std::string& name, const LLUUID& folder) : mName(name), mFolder(folder) {}
virtual ~ObjectNameMatches() {}
bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) override
{
return item && item->getParentUUID() == mFolder && item->getName() == mName;
}
private:
const std::string& mName;
const LLUUID& mFolder;
};
static LLUUID invfolderid;
static LLUUID getAssetIDByName(const std::string& name)
{
if (name.empty()) return LLUUID::null;
LLViewerInventoryCategory::cat_array_t cats;
LLViewerInventoryItem::item_array_t items;
ObjectNameMatches matches(name, invfolderid);
gInventory.collectDescendentsIf(LLUUID::null, cats, items, false, matches, true);
return items.empty() ? LLUUID(name) : items[0]->getAssetUUID();
};
void AOSystem::start()
{
llassert(!instanceExists() && !AOInvTimer::instanceExists()); // This should only be run once!
auto control(gSavedSettings.getControl("AOEnabled"));
if (control->get()) AOInvTimer::createIfNeeded(); // Start the clock
control->getSignal()->connect([](LLControlVariable*, const LLSD& enabled) {
if (enabled.asBoolean()) AOInvTimer::createIfNeeded(); // Start the clock
else deleteSingleton();
});
}
// STUFF -------------------------------------------------------
LLFloaterAO::LLFloaterAO(const LLSD&) : LLFloater("floater_ao")
, mCombos({})
{
@@ -288,15 +171,17 @@ void LLFloaterAO::onComboBoxCommit(LLUICtrl* ctrl) const
}
else
{
AOState state = getStateFromCombo(box);
llassert(state != STATE_AGENT_END);
const LLUUID& anim = getAssetIDByName(stranim);
auto end = mCombos.end();
auto it = std::find(mCombos.begin(), end, box);
llassert(it != end);
const auto state = std::distance(mCombos.begin(), it);
if (auto aop = ao->mAOOverrides[state])
{
LLUUID getAssetIDByName(const std::string & name);
// LL_INFOS() << "state " << state << " - " << aop->playing() << LL_ENDL;
bool was_playing = aop->playing;
if (was_playing) aop->play(false);
aop->ao_id = anim;
aop->ao_id = getAssetIDByName(stranim);
if (was_playing) aop->play();
}
}
@@ -322,15 +207,12 @@ void LLFloaterAO::onClickReloadCard() const
void LLFloaterAO::onClickOpenCard() const
{
if (!AOInvTimer::needed())
{
LLUUID config_nc_id = (LLUUID)gSavedPerAccountSettings.getString("AOConfigNotecardID");
if (config_nc_id.notNull())
if (LLViewerInventoryItem* item = gInventory.getItem(config_nc_id))
if (gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE) || gAgent.isGodlike())
if(!item->getAssetUUID().isNull())
open_notecard(item, "Note: " + item->getName(), LLUUID::null, false);
}
auto config_nc_id = (LLUUID)gSavedPerAccountSettings.getString("AOConfigNotecardID");
if (config_nc_id.notNull())
if (LLViewerInventoryItem* item = gInventory.getItem(config_nc_id))
if (gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE) || gAgent.isGodlike())
if(!item->getAssetUUID().isNull())
open_notecard(item, "Note: " + item->getName(), LLUUID::null, false);
}
void LLFloaterAO::onClickNewCard() const
@@ -350,464 +232,3 @@ void LLFloaterAO::onClickNewCard() const
"Drop this notecard in your AO window to use", LLAssetType::AT_NOTECARD,
LLInventoryType::IT_NOTECARD, NOT_WEARABLE, PERM_ALL, new AONotecardCallback(ao_template));
}
AOState LLFloaterAO::getStateFromCombo(const LLComboBox* combo) const
{
if (combo)
{
const auto& end = mCombos.end();
const auto it = std::find(mCombos.begin(), end, combo);
if (it != end) return (AOState)std::distance(mCombos.begin(), it);
}
return STATE_AGENT_END;
}
AOSystem::overrides::overrides(const char* setting_name)
: setting(setting_name ? gSavedPerAccountSettings.getControl("AODefault" + std::string(setting_name)) : nullptr)
, playing(false)
{
}
bool AOSystem::overrides::play_condition() const
{
// Stop stand first then play
instance().stopCurrentStand();
return false;
}
void AOSystem::overrides::play(bool start)
{
if (playing == start) return;
// LL_INFOS() << "st" << (start ? "art" : "op") << " override: " << aop->getOverride() << LL_ENDL;
// We can always stop, but starting is particular
if (start)
{
if (play_condition())
return;
}
else if (playing && !isLowPriority()) // Stands were turned off if this isn't a low priority overrides
{
if (!isAgentAvatarValid() && !gAgentAvatarp->isSitting() && !gAgent.getFlying()) // check if sitting or hovering
instance().stand().play();
}
playing = start;
gAgent.sendAnimationRequest(ao_id, start ? ANIM_REQUEST_START : ANIM_REQUEST_STOP);
}
bool AOSystem::override_stand::play_condition() const
{
// Do not call base, stands do their own thing
// stands have lowest priority
return (!isAgentAvatarValid() || gAgentAvatarp->isSitting())
&& (gAgentCamera.cameraMouselook() && gSavedSettings.getBOOL("AONoStandsInMouselook"));
}
bool AOSystem::override_sit::play_condition() const
{
overrides::play_condition();
return !gSavedSettings.getBOOL("AOSitsEnabled");
}
bool AOSystem::override_stand::overrideAnim(bool swimming, const LLUUID& anim) const
{
return anim == ANIM_AGENT_STAND;
}
static AOState getSitType()
{
return gAgentAvatarp->getParent() ? STATE_AGENT_SIT : STATE_AGENT_SIT_GROUND;
}
AOSystem::AOSystem()
: stand_iterator(0)
, mAOOverrides({})
{
// TODO: When we move to C++20 and when GCC and MSVC support it (at the time of writing, neither fully do), use __VA_OPT__ here instead.
#define ANY_OVERRIDE(which, state, ...) mAOOverrides[STATE_AGENT_##state] = new which(ANIM_AGENT_##state, ##__VA_ARGS__)
#define BASIC_OVERRIDE(state, ...) ANY_OVERRIDE(override_single, ##state, ##__VA_ARGS__)
mAOOverrides[STATE_AGENT_IDLE] = new override_stand();
BASIC_OVERRIDE(WALK, "Walk");
BASIC_OVERRIDE(RUN, "Run");
BASIC_OVERRIDE(PRE_JUMP, "PreJump");
BASIC_OVERRIDE(JUMP, "Jump");
BASIC_OVERRIDE(TURNLEFT);
BASIC_OVERRIDE(TURNRIGHT);
#define SIT_OVERRIDE(control, state, ...) mAOOverrides[STATE_AGENT_##state] = new override_sit(uuid_set_t{ANIM_AGENT_##state, __VA_ARGS__}, control)
SIT_OVERRIDE("Sit", SIT, ANIM_AGENT_SIT_FEMALE, ANIM_AGENT_SIT_GENERIC);
SIT_OVERRIDE("GroundSit", SIT_GROUND, ANIM_AGENT_SIT_GROUND_CONSTRAINED);
#undef SIT_OVERRIDE
BASIC_OVERRIDE(HOVER, "Hover", false);
BASIC_OVERRIDE(HOVER_DOWN, "FlyDown", false);
BASIC_OVERRIDE(HOVER_UP, "FlyUp", false);
BASIC_OVERRIDE(CROUCH, "Crouch");
BASIC_OVERRIDE(CROUCHWALK, "CrouchWalk");
BASIC_OVERRIDE(FALLDOWN, "Fall");
BASIC_OVERRIDE(STANDUP, "StandUp");
BASIC_OVERRIDE(LAND, "Land");
BASIC_OVERRIDE(FLY, "Fly", false);
BASIC_OVERRIDE(FLYSLOW, "FlySlow", false);
ANY_OVERRIDE(override_low_priority, TYPE, "Typing");
mAOOverrides[STATE_AGENT_SWIM_DOWN] = new override_single(ANIM_AGENT_HOVER_DOWN, "SwimDown", true);
mAOOverrides[STATE_AGENT_SWIM_UP] = new override_single(ANIM_AGENT_HOVER_UP, "SwimUp", true);
mAOOverrides[STATE_AGENT_SWIM] = new override_single(ANIM_AGENT_FLY, "Swim", true);
mAOOverrides[STATE_AGENT_FLOAT] = new override_single(ANIM_AGENT_HOVER, "Float", true);
#undef BASIC_OVERRIDE
#undef ANY_OVERRIDE
auto swim_forced = gSavedSettings.getControl("AOSwimForced");
sSwimming = swim_forced->get().asBoolean() || is_underwater();
mConnections[0] = gSavedSettings.getControl("AOSitsEnabled")->getSignal()->connect([this](LLControlVariable*, const LLSD& val) {
if (!isAgentAvatarValid() || !gAgentAvatarp->isSitting()) return;
gAgent.sendAnimationRequest(mAOOverrides[getSitType()]->ao_id, val.asBoolean() ? ANIM_REQUEST_START : ANIM_REQUEST_STOP);
});
const auto& swim_cb = [=](LLControlVariable*, const LLSD&){ toggleSwim(swim_forced->get().asBoolean() || is_underwater()); };
auto swim_enabled = gSavedSettings.getControl("AOSwimEnabled");
mConnections[1] = swim_enabled->getSignal()->connect(swim_cb);
mConnections[2] = swim_forced->getSignal()->connect([swim_cb, swim_enabled](LLControlVariable*, const LLSD& val) {
if (val.asBoolean()) // Automatically enable Swim AO.
swim_enabled->set(true);
swim_cb(nullptr, LLSD());
});
}
AOSystem::~AOSystem()
{
stopAllOverrides();
for (auto& ao : mAOOverrides)
{
if (ao)
{
delete ao;
ao = nullptr;
}
}
}
void AOSystem::requestConfigNotecard(bool reload)
{
LLUUID configncitem = (LLUUID)gSavedPerAccountSettings.getString("AOConfigNotecardID");
if (configncitem.notNull())
{
bool success = false;
if (const LLInventoryItem* item = gInventory.getItem(configncitem))
{
if (gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE) || gAgent.isGodlike())
{
if (item->getAssetUUID().notNull())
{
invfolderid = item->getParentUUID();
gAssetStorage->getInvItemAsset(LLHost::invalid,
gAgentID,
gAgentSessionID,
item->getPermissions().getOwner(),
LLUUID::null,
item->getUUID(),
item->getAssetUUID(),
item->getType(),
[reload](LLVFS* vfs, const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status)
{
parseNotecard(vfs, asset_uuid, type, status, reload);
},
nullptr,
true);
success = true;
}
}
}
if (!success) cmdline_printchat("Could not read the specified Config Notecard");
}
}
void AOSystem::initSingleton()
{
mAOStands.clear();
requestConfigNotecard();
}
void AOSystem::typing(bool start)
{
uuid_vec_t anims;
// If we're stopping, stop regardless, just in case the setting was toggled during (e.g.: keyboard shortcut)
if (!start || gSavedSettings.getBOOL("PlayTypingAnim")) // Linden typing
anims.push_back(ANIM_AGENT_TYPE);
if (const auto& ao = getIfExists()) // Typing override
anims.push_back(ao->mAOOverrides[STATE_AGENT_TYPE]->getOverride());
if (anims.empty()) return;
gAgent.sendAnimationRequests(anims, start ? ANIM_REQUEST_START : ANIM_REQUEST_STOP);
}
AOState GetStateFromToken(const std::string& strtoken)
{
if (strtoken == "[ Sitting On Ground ]") return STATE_AGENT_SIT_GROUND;
if (strtoken == "[ Sitting ]") return STATE_AGENT_SIT;
if (strtoken == "[ Crouching ]") return STATE_AGENT_CROUCH;
if (strtoken == "[ Crouch Walking ]") return STATE_AGENT_CROUCHWALK;
if (strtoken == "[ Standing Up ]") return STATE_AGENT_STANDUP;
if (strtoken == "[ Falling ]") return STATE_AGENT_FALLDOWN;
if (strtoken == "[ Flying Down ]") return STATE_AGENT_HOVER_DOWN;
if (strtoken == "[ Flying Up ]") return STATE_AGENT_HOVER_UP;
if (strtoken == "[ Flying Slow ]") return STATE_AGENT_FLYSLOW;
if (strtoken == "[ Flying ]") return STATE_AGENT_FLY;
if (strtoken == "[ Hovering ]") return STATE_AGENT_HOVER;
if (strtoken == "[ Jumping ]") return STATE_AGENT_JUMP;
if (strtoken == "[ Pre Jumping ]") return STATE_AGENT_PRE_JUMP;
if (strtoken == "[ Running ]") return STATE_AGENT_RUN;
if (strtoken == "[ Turning Right ]") return STATE_AGENT_TURNRIGHT;
if (strtoken == "[ Turning Left ]") return STATE_AGENT_TURNLEFT;
if (strtoken == "[ Walking ]") return STATE_AGENT_WALK;
if (strtoken == "[ Landing ]") return STATE_AGENT_LAND;
if (strtoken == "[ Standing ]") return STATE_AGENT_IDLE;
if (strtoken == "[ Swimming Down ]") return STATE_AGENT_SWIM_DOWN;
if (strtoken == "[ Swimming Up ]") return STATE_AGENT_SWIM_UP;
if (strtoken == "[ Swimming Forward ]") return STATE_AGENT_SWIM;
if (strtoken == "[ Floating ]") return STATE_AGENT_FLOAT;
if (strtoken == "[ Typing ]") return STATE_AGENT_TYPE;
return STATE_AGENT_END;
}
void AOSystem::updateStand()
{
auto& stand_ao = stand();
bool is_standing = stand_ao.playing;
if (is_standing) stand_ao.play(false); // Stop stand first
stand_ao.update(mAOStands, stand_iterator); // Now update stand
if (is_standing && !mAOStands.empty()) // Play stand if needed
stand_ao.play();
}
int AOSystem::cycleStand(bool next, bool random)
{
if (mAOStands.empty()) return -1;
const int size = mAOStands.size();
if (size > 1)
{
if (random && gSavedSettings.getBOOL("AOStandRandomize"))
for (auto previous = stand_iterator; previous == stand_iterator;
stand_iterator = ll_rand(size));
else
{
stand_iterator += next ? 1 : -1;
// Wrap around
if (stand_iterator == size) stand_iterator = 0;
else if (stand_iterator == -1) stand_iterator = size - 1;
}
if (auto floater = LLFloaterAO::findInstance())
if (auto combo = floater->getComboFromState(STATE_AGENT_IDLE))
combo->selectNthItem(stand_iterator);
// LL_INFOS() << "changing stand to " << mAOStands[stand_iterator].anim_name << LL_ENDL;
}
updateStand();
return stand_iterator;
}
void AOSystem::toggleSwim(bool underwater)
{
sSwimming = underwater && enable_swim();
if (isStanding()) return; // Don't bother if we're just standing (Who pushed us?!)
typedef std::array<overrides*, 4> flies_t;
// Stop all of the previous states
#define AOO(state) mAOOverrides[STATE_AGENT_##state]
const flies_t swims = { AOO(FLOAT), AOO(SWIM), AOO(SWIM_UP), AOO(SWIM_DOWN) };
const flies_t flies = { AOO(HOVER), AOO(FLY), AOO(HOVER_UP), AOO(HOVER_DOWN) };
#undef AOO
const auto& oldaos = sSwimming ? flies : swims;
const auto& newaos = sSwimming ? swims : flies;
for (auto i = 0; i < oldaos.size(); ++i)
{
auto& old = *oldaos[i];
if (old.playing)
{
old.play(false);
newaos[i]->play();
}
}
}
void AOSystem::doMotion(const LLUUID& id, bool start)
{
if (id.isNull() || !gAgentAvatarp) return;
overrides* aop = nullptr;
AOState state = STATE_AGENT_IDLE;
for (U8 i = STATE_AGENT_IDLE; !aop && i < STATE_AGENT_END; ++i)
{
auto& ao = mAOOverrides[i];
if (ao && ao->overrideAnim(sSwimming, id))
{
aop = ao;
state = (AOState)i;
}
}
if (aop)
{
// LL_INFOS() << "st" << (start ? "art" : "op") << " anim " << id << " state " << state << LL_ENDL;
aop->play(start);
}
}
void AOSystem::stopAllOverrides() const
{
if (!isAgentAvatarValid()) return;
uuid_vec_t anims;
for (auto& ao : mAOOverrides)
{
if (ao->playing)
{
anims.push_back(ao->getOverride());
ao->playing = false;
}
}
for (const auto& stand : mAOStands)
anims.push_back(stand.ao_id);
gAgent.sendAnimationRequests(anims, ANIM_REQUEST_STOP);
}
void AOSystem::parseNotecard(LLVFS *vfs, const LLUUID& asset_uuid, LLAssetType::EType type, S32 status, bool reload)
{
if (status == LL_ERR_NOERR)
{
if (type == LLAssetType::AT_NOTECARD)
{
AOSystem* self = getIfExists();
S32 size = vfs->getSize(asset_uuid, LLAssetType::AT_NOTECARD);
U8* buffer = new U8[size];
vfs->getData(asset_uuid, type, buffer, 0, size);
LLMemoryStream str(buffer, size);
LLNotecard nc;
if (nc.importStream(str))
{
LL_INFOS() << "ao nc decode success" << LL_ENDL;
if (self && reload) self->stopAllOverrides();
auto floater = LLFloaterAO::findInstance();
if (floater)
for (auto& combo : floater->mCombos)
{
if (combo)
{
combo->clear();
combo->removeall();
}
}
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
boost::char_separator<char> sep("\n");
tokenizer tokline(nc.getText(), sep);
for (const auto& strline : tokline)
{
// LL_INFOS() << "uncommented line: " << strline << LL_ENDL;
boost::regex type("^(\\s*)(\\[ )(.*)( \\])");
boost::smatch what;
if (boost::regex_search(strline, what, type))
{
const std::string strtoken(what[0]);
const AOState state = GetStateFromToken(strtoken);
if (state == STATE_AGENT_END)
{
LL_WARNS() << "Invalid token: " << strtoken << LL_ENDL;
continue;
}
// LL_INFOS() << "type: " << strtoken << LL_ENDL;
LLComboBox* combo = floater ? floater->getComboFromState(state) : nullptr;
auto aop = (reload && self) ? self->mAOOverrides[state] : nullptr;
// LL_INFOS() << "anims in type: " << boost::regex_replace(strline, type, "") << LL_ENDL;
boost::char_separator<char> sep("|,");
std::string stranimnames(boost::regex_replace(strline, type, ""));
tokenizer tokanimnames(stranimnames, sep);
for (const auto& stranim : tokanimnames)
{
if (stranim.empty()) continue;
const auto& animid(getAssetIDByName(stranim));
// LL_INFOS() << invfolderid.asString().c_str() << LL_ENDL;
// LL_INFOS() << "anim: " << stranim.c_str() << " assetid: " << animid << LL_ENDL;
if (animid.notNull())
{
if (aop) // If we're reloading
{
if (state == STATE_AGENT_IDLE)
self->mAOStands.push_back({ animid, stranim });
else
aop->ao_id = animid;
}
if (combo && !combo->selectByValue(stranim)) // check if exists
combo->add(stranim, ADD_BOTTOM, true);
}
else cmdline_printchat("Warning: animation '" + stranim + "' could not be found (Section: " + strtoken + ").");
}
}
}
LL_INFOS() << "ao nc read sucess" << LL_ENDL;
if (self)
{
if (auto combo = floater ? floater->getComboFromState(STATE_AGENT_IDLE) : nullptr)
combo->selectNthItem(self->stand_iterator);
for (U8 i = STATE_AGENT_IDLE+1; i < STATE_AGENT_END; ++i)
{
auto& aop = self->mAOOverrides[i];
if (!aop) continue;
auto& ao = *aop;
const auto& setting = ao.setting;
if (!setting && ao.ao_id.isNull()) continue;
const auto& defaultanim = setting ? setting->get().asStringRef() : ao.ao_id.asString();
if (defaultanim.empty()) continue;
const LLUUID& ao_id = getAssetIDByName(defaultanim);
if (reload && ao_id.notNull()) ao.ao_id = ao_id;
if (LLComboBox* combo = floater ? floater->getComboFromState(i) : nullptr)
if (!combo->selectByValue(defaultanim))
combo->add(defaultanim, ADD_BOTTOM, true);
}
if (reload && isAgentAvatarValid())
{
// Fix the stand iter, container size may have changed
auto& iter = self->stand_iterator;
const auto& stands = self->mAOStands;
iter = llmin(iter, (int)stands.size()-1);
// Update the current stand
self->stand().update(stands, iter);
self->mAOStandTimer.reset();
const auto& anims = gAgentAvatarp->mPlayingAnimations;
bool playing = false;
for (auto& aop : self->mAOOverrides)
{
for (const auto& anim : anims)
{
if (aop && aop->overrideAnim(sSwimming, anim.first))
{
if (!aop->isLowPriority()) playing = true;
aop->play();
break;
}
}
}
if (!playing) self->stand().play(); // Play stand if nothing was being played, this sometimes happens
// Toggle typing AO the moment we toggle AO
typing(gAgent.getRenderState() & AGENT_STATE_TYPING);
}
}
}
else LL_INFOS() << "ao nc decode error" << LL_ENDL;
delete[] buffer;
}
}
else LL_INFOS() << "ao nc read error" << LL_ENDL;
}

View File

@@ -1,71 +1,8 @@
#pragma once
#ifndef LL_LLFLOATERAO_H
#define LL_LLFLOATERAO_H
#include "llagent.h"
#include "llcontrol.h"
#include "lleventtimer.h"
#include "aostate.h"
#include "llfloater.h"
enum AOState : U8
{
STATE_AGENT_IDLE ,
STATE_AGENT_WALK,
STATE_AGENT_RUN,
STATE_AGENT_PRE_JUMP,
STATE_AGENT_JUMP,
STATE_AGENT_TURNLEFT,
STATE_AGENT_TURNRIGHT,
STATE_AGENT_SIT,
STATE_AGENT_SIT_GROUND,
STATE_AGENT_HOVER,
STATE_AGENT_HOVER_DOWN,
STATE_AGENT_HOVER_UP,
STATE_AGENT_CROUCH,
STATE_AGENT_CROUCHWALK,
STATE_AGENT_FALLDOWN,
STATE_AGENT_STANDUP,
STATE_AGENT_LAND,
STATE_AGENT_FLY,
STATE_AGENT_FLYSLOW,
STATE_AGENT_TYPE,
STATE_AGENT_SWIM_DOWN,
STATE_AGENT_SWIM_UP,
STATE_AGENT_SWIM,
STATE_AGENT_FLOAT,
STATE_AGENT_END
};
class AOStandTimer final : public LLEventTimer
{
friend class AOSystem;
public:
AOStandTimer();
~AOStandTimer();
BOOL tick() override;
void reset();
};
class AOInvTimer final : public LLEventTimer, public LLSingleton<AOInvTimer>
{
friend class LLSingleton<AOInvTimer>;
friend class AOSystem;
AOInvTimer();
~AOInvTimer();
static void createIfNeeded();
public:
static bool needed();
BOOL tick() override;
};
class LLFloaterAO final : public LLFloater, public LLFloaterSingleton<LLFloaterAO>
{
friend class AOSystem;
@@ -89,111 +26,5 @@ private:
protected:
AOState getStateFromCombo(const class LLComboBox* combo) const;
LLComboBox* getComboFromState(const U8& state) const { return mCombos[state]; }
};
class AOSystem final : public LLSingleton<AOSystem>
{
friend class LLSingleton<AOSystem>;
friend class AOInvTimer;
friend class LLFloaterAO;
AOSystem();
~AOSystem();
public:
static void start(); // Runs the necessary actions to get the AOSystem ready, then initializes it.
void initSingleton() override;
static void typing(bool start);
int stand_iterator;
bool isStanding() const { return stand().playing; }
void updateStand();
int cycleStand(bool next = true, bool random = true);
void toggleSwim(bool underwater);
void doMotion(const LLUUID& id, bool start);
void startMotion(const LLUUID& id) { doMotion(id, true); }
void stopMotion(const LLUUID& id) { doMotion(id, false); }
void stopCurrentStand() const { stand().play(false); }
void stopAllOverrides() const;
protected:
struct struct_stands
{
LLUUID ao_id;
std::string anim_name;
};
typedef std::vector<struct_stands> stands_vec;
stands_vec mAOStands;
struct overrides
{
virtual bool overrideAnim(bool swimming, const LLUUID& anim) const = 0;
virtual const LLUUID& getOverride() const { return ao_id; }
virtual bool play_condition() const; // True if prevented from playing
virtual bool isLowPriority() const { return false; }
void play(bool start = true);
LLUUID ao_id;
LLPointer<LLControlVariable> setting;
bool playing;
virtual ~overrides() {}
protected:
overrides(const char* setting_name);
};
struct override_low_priority final : public overrides
{
override_low_priority(const LLUUID& id, const char* setting_name = nullptr)
: overrides(setting_name), orig_id(id) {}
bool overrideAnim(bool swimming, const LLUUID & anim) const override { return orig_id == anim; }
bool play_condition() const override { return false; }
bool isLowPriority() const override { return true; }
private:
const LLUUID orig_id;
};
struct override_single final : public overrides
{
override_single(const LLUUID& id, const char* setting_name = nullptr, U8 swim = 2)
: overrides(setting_name), orig_id(id), swim(swim) {}
bool overrideAnim(bool swimming, const LLUUID& anim) const override { return (swim == 2 || !!swim == swimming) && orig_id == anim; }
private:
const LLUUID orig_id;
const U8 swim; // 2 = irrelevant, 0 = flying, 1 = swimming
};
struct override_stand final : public overrides
{
override_stand() : overrides(nullptr) {}
bool overrideAnim(bool swimming, const LLUUID& anim) const override;
bool play_condition() const override;
bool isLowPriority() const override { return true; }
void update(const stands_vec& stands, const int& iter)
{
if (stands.empty()) ao_id.setNull();
else ao_id = stands[iter].ao_id;
}
};
struct override_sit final : public overrides
{
override_sit(const uuid_set_t& ids, const char* setting_name = nullptr)
: overrides(setting_name)
, orig_ids(ids)
{}
bool overrideAnim(bool swimming, const LLUUID& anim) const override { return orig_ids.find(anim) != orig_ids.end(); }
bool play_condition() const override;
private:
const uuid_set_t orig_ids;
};
std::array<overrides*, STATE_AGENT_END> mAOOverrides;
override_stand& stand() const { return static_cast<override_stand&>(*mAOOverrides[STATE_AGENT_IDLE]); }
private:
std::array<boost::signals2::scoped_connection, 3> mConnections;
AOStandTimer mAOStandTimer;
static void requestConfigNotecard(bool reload = true);
static void parseNotecard(LLVFS* vfs, const LLUUID& asset_uuid, LLAssetType::EType type, S32 status, bool reload);
};
#endif

View File

@@ -106,7 +106,7 @@
#include "lluictrlfactory.h" //For LLUICtrlFactory::getLayeredXMLNode
#include "floaterao.h" // for Typing override
#include "aosystem.h" // for Typing override
#include "hippolimits.h" // for getMaxAgentGroups
// [RLVa:KB] - Checked: 2011-11-04 (RLVa-1.4.4a)
#include "rlvactions.h"

View File

@@ -29,6 +29,7 @@
#include "pipeline.h"
#include "aosystem.h" //For AOSystem
#include "llagent.h"
#include "llanimationstates.h"
#include "llfloatercamera.h"
@@ -49,7 +50,6 @@
#include "llwindow.h"
#include "llworld.h"
#include "llfloatertools.h" //For gFloaterTools
#include "floaterao.h" //For LLFloaterAO
#include "llfloatercustomize.h" //For gFloaterCustomize
// [RLVa:KB] - Checked: 2010-05-10 (RLVa-1.2.0g)
#include "rlvhandler.h"

View File

@@ -159,9 +159,9 @@
// Included so that constants/settings might be initialized
// in save_settings_to_globals()
#include "aosystem.h"
#include "llbutton.h"
#include "llcombobox.h"
#include "floaterao.h"
#include "floaterlocalassetbrowse.h"
#include "llsurface.h"
#include "llvotree.h"

View File

@@ -52,9 +52,9 @@
#include "llaudioengine_openal.h"
#endif
#include "aosystem.h"
#include "hippogridmanager.h"
#include "hippolimits.h"
#include "floaterao.h"
#include "statemachine/aifilepicker.h"
#include "lfsimfeaturehandler.h"

View File

@@ -109,8 +109,8 @@
#include "llvoicevisualizer.h" // Ventrella
#include "llsdserialize.h" //For the client definitions
#include "aosystem.h"
#include "llcachename.h"
#include "floaterao.h"
#include "llsdutil.h"
#include "llskinningutil.h"