From b3cd0a96b5ebfa38b041075d3e35c2de1b7f4f8a Mon Sep 17 00:00:00 2001 From: Rye Mutt <rye@alchemyviewer.org> Date: Tue, 24 Mar 2020 07:39:14 -0400 Subject: [PATCH] Add chat commands --- indra/newview/CMakeLists.txt | 2 + indra/newview/alchatcommand.cpp | 340 ++++++++++++++++++ indra/newview/alchatcommand.h | 30 ++ .../newview/app_settings/settings_alchemy.xml | 11 + indra/newview/llchatbar.cpp | 5 +- indra/newview/llfloaterimnearbychat.cpp | 3 +- indra/newview/llgesturemgr.cpp | 8 +- indra/newview/llregioninfomodel.h | 2 +- indra/newview/llviewergesture.cpp | 4 +- 9 files changed, 398 insertions(+), 7 deletions(-) create mode 100644 indra/newview/alchatcommand.cpp create mode 100644 indra/newview/alchatcommand.h diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 8f47dcfad2d..3d2ede71693 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -110,6 +110,7 @@ include_directories(SYSTEM ) set(viewer_SOURCE_FILES + alchatcommand.cpp alunzip.cpp groupchatlistener.cpp llaccountingcostmanager.cpp @@ -746,6 +747,7 @@ set(viewer_HEADER_FILES CMakeLists.txt ViewerInstall.cmake alunzip.h + alchatcommand.h groupchatlistener.h llaccountingcost.h llaccountingcostmanager.h diff --git a/indra/newview/alchatcommand.cpp b/indra/newview/alchatcommand.cpp new file mode 100644 index 00000000000..3c723014a0f --- /dev/null +++ b/indra/newview/alchatcommand.cpp @@ -0,0 +1,340 @@ +/** +* @file alchatcommand.cpp +* @brief ALChatCommand implementation for chat input commands +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Copyright (C) 2013 Drake Arconis +* +* 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. +* $/LicenseInfo$ +**/ + +#include "llviewerprecompiledheaders.h" + +#include "alchatcommand.h" + +// lib includes +#include "llcalc.h" +#include "llparcel.h" +#include "llstring.h" +#include "material_codes.h" +#include "object_flags.h" + +// viewer includes +#include "llagent.h" +#include "llagentcamera.h" +#include "llagentui.h" +//#include "llaoengine.h" +#include "llcommandhandler.h" +#include "llfloaterimnearbychat.h" +#include "llfloaterreg.h" +#include "llfloaterregioninfo.h" +#include "llnotificationsutil.h" +#include "llregioninfomodel.h" +#include "llstartup.h" +#include "lltrans.h" +#include "llviewercontrol.h" +#include "llviewermessage.h" +#include "llviewerobjectlist.h" +#include "llviewerparcelmgr.h" +#include "llviewerregion.h" +#include "llvoavatarself.h" +#include "llvolume.h" +#include "llvolumemessage.h" + +bool ALChatCommand::parseCommand(std::string data) +{ + static LLCachedControl<bool> enableChatCmd(gSavedSettings, "AlchemyChatCommandEnable", true); + if (enableChatCmd) + { + utf8str_tolower(data); + std::istringstream input(data); + std::string cmd; + + if (!(input >> cmd)) return false; + + static LLCachedControl<std::string> sDrawDistanceCommand(gSavedSettings, "AlchemyChatCommandDrawDistance", "/dd"); + static LLCachedControl<std::string> sHeightCommand(gSavedSettings, "AlchemyChatCommandHeight", "/gth"); + static LLCachedControl<std::string> sGroundCommand(gSavedSettings, "AlchemyChatCommandGround", "/flr"); + static LLCachedControl<std::string> sPosCommand(gSavedSettings, "AlchemyChatCommandPos", "/pos"); + static LLCachedControl<std::string> sRezPlatCommand(gSavedSettings, "AlchemyChatCommandRezPlat", "/plat"); + static LLCachedControl<std::string> sHomeCommand(gSavedSettings, "AlchemyChatCommandHome", "/home"); + static LLCachedControl<std::string> sSetHomeCommand(gSavedSettings, "AlchemyChatCommandSetHome", "/sethome"); + static LLCachedControl<std::string> sCalcCommand(gSavedSettings, "AlchemyChatCommandCalc", "/calc"); + static LLCachedControl<std::string> sMaptoCommand(gSavedSettings, "AlchemyChatCommandMapto", "/mapto"); + static LLCachedControl<std::string> sClearCommand(gSavedSettings, "AlchemyChatCommandClearNearby", "/clr"); + static LLCachedControl<std::string> sRegionMsgCommand(gSavedSettings, "AlchemyChatCommandRegionMessage", "/regionmsg"); + static LLCachedControl<std::string> sSetNearbyChatChannelCmd(gSavedSettings, "AlchemyChatCommandSetChatChannel", "/setchannel"); + static LLCachedControl<std::string> sResyncAnimCommand(gSavedSettings, "AlchemyChatCommandResyncAnim", "/resync"); + static LLCachedControl<std::string> sTeleportToCam(gSavedSettings, "AlchemyChatCommandTeleportToCam", "/tp2cam"); + static LLCachedControl<std::string> sHoverHeight(gSavedSettings, "AlchemyChatCommandHoverHeight", "/hover"); + static LLCachedControl<std::string> sAOCommand(gSavedSettings, "AlchemyChatCommandAnimationOverride", "/ao"); + + if (cmd == utf8str_tolower(sDrawDistanceCommand)) // dd + { + F32 dist; + if (input >> dist) + { + dist = llclamp(dist, 16.f, 512.f); + gSavedSettings.setF32("RenderFarClip", dist); + gAgentCamera.mDrawDistance = dist; + return true; + } + } + else if (cmd == utf8str_tolower(sHeightCommand)) // gth + { + F64 z; + if (input >> z) + { + LLVector3d pos_global = gAgent.getPositionGlobal(); + pos_global.mdV[VZ] = z; + gAgent.teleportViaLocation(pos_global); + return true; + } + } + else if (cmd == utf8str_tolower(sGroundCommand)) // flr + { + LLVector3d pos_global = gAgent.getPositionGlobal(); + pos_global.mdV[VZ] = 0.0; + gAgent.teleportViaLocation(pos_global); + return true; + } + else if (cmd == utf8str_tolower(sPosCommand)) // pos + { + F64 x, y, z; + if ((input >> x) && (input >> y) && (input >> z)) + { + LLViewerRegion* regionp = gAgent.getRegion(); + if (regionp) + { + LLVector3d target_pos = regionp->getPosGlobalFromRegion(LLVector3((F32) x, (F32) y, (F32) z)); + gAgent.teleportViaLocation(target_pos); + } + return true; + } + } + else if (cmd == utf8str_tolower(sRezPlatCommand)) // plat + { + F32 size; + if (!(input >> size)) + size = static_cast<F32>(gSavedSettings.getF32("AlchemyChatCommandRezPlatSize")); + + const LLVector3& agent_pos = gAgent.getPositionAgent(); + const LLVector3 rez_pos(agent_pos.mV[VX], agent_pos.mV[VY], agent_pos.mV[VZ] - ((gAgentAvatarp->getScale().mV[VZ] / 2.f) + 0.25f + (gAgent.getVelocity().magVec() * 0.333f))); + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_ObjectAdd); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + LLUUID group_id = gAgent.getGroupID(); + if (gSavedSettings.getBOOL("AlchemyRezUnderLandGroup")) + { + LLParcel* land_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + // Is the agent in the land group + if (gAgent.isInGroup(land_parcel->getGroupID())) + group_id = land_parcel->getGroupID(); + // Is the agent in the land group (the group owns the land) + else if (gAgent.isInGroup(land_parcel->getOwnerID())) + group_id = land_parcel->getOwnerID(); + } + msg->addUUIDFast(_PREHASH_GroupID, group_id); + msg->nextBlockFast(_PREHASH_ObjectData); + msg->addU8Fast(_PREHASH_PCode, LL_PCODE_VOLUME); + msg->addU8Fast(_PREHASH_Material, LL_MCODE_STONE); + msg->addU32Fast(_PREHASH_AddFlags, agent_pos.mV[VZ] > 4096.f ? FLAGS_CREATE_SELECTED : 0U); + + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + volume_params.setBeginAndEndS(0.f, 1.f); + volume_params.setBeginAndEndT(0.f, 1.f); + volume_params.setRatio(1.f, 1.f); + volume_params.setShear(0.f, 0.f); + LLVolumeMessage::packVolumeParams(&volume_params, msg); + + msg->addVector3Fast(_PREHASH_Scale, LLVector3(size, size, 0.25f)); + msg->addQuatFast(_PREHASH_Rotation, LLQuaternion()); + msg->addVector3Fast(_PREHASH_RayStart, rez_pos); + msg->addVector3Fast(_PREHASH_RayEnd, rez_pos); + msg->addUUIDFast(_PREHASH_RayTargetID, LLUUID::null); + msg->addU8Fast(_PREHASH_BypassRaycast, TRUE); + msg->addU8Fast(_PREHASH_RayEndIsIntersection, FALSE); + msg->addU8Fast(_PREHASH_State, FALSE); + msg->sendReliable(gAgent.getRegionHost()); + + return true; + } + else if (cmd == utf8str_tolower(sHomeCommand)) // home + { + gAgent.teleportHome(); + return true; + } + else if (cmd == utf8str_tolower(sSetHomeCommand)) // sethome + { + gAgent.setStartPosition(START_LOCATION_ID_HOME); + return true; + } + else if (cmd == utf8str_tolower(sCalcCommand)) // calc + { + if (data.length() > cmd.length() + 1) + { + F32 result = 0.f; + std::string expr = data.substr(cmd.length() + 1); + LLStringUtil::toUpper(expr); + if (LLCalc::getInstance()->evalString(expr, result)) + { + LLSD args; + args["EXPRESSION"] = expr; + args["RESULT"] = result; + LLNotificationsUtil::add("ChatCommandCalc", args); + return true; + } + LLNotificationsUtil::add("ChatCommandCalcFailed"); + return true; + } + } + else if (cmd == utf8str_tolower(sMaptoCommand)) // mapto + { + const std::string::size_type length = cmd.length() + 1; + if (data.length() > length) + { + const LLVector3d& pos = gAgent.getPositionGlobal(); + LLSD params; + params.append(data.substr(length)); + params.append(fmodf(static_cast<F32>(pos.mdV[VX]), REGION_WIDTH_METERS)); + params.append(fmodf(static_cast<F32>(pos.mdV[VY]), REGION_WIDTH_METERS)); + params.append(fmodf(static_cast<F32>(pos.mdV[VZ]), REGION_HEIGHT_METERS)); + LLCommandDispatcher::dispatch("teleport", params, LLSD(), nullptr, "clicked", true); + return true; + } + } + else if (cmd == utf8str_tolower(sClearCommand)) + { + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"); + if (nearby_chat) + { + nearby_chat->reloadMessages(true); + } + return true; + } + else if (cmd == "/droll") + { + S32 dice_sides; + if (!(input >> dice_sides)) + dice_sides = 6; + LLSD args; + args["RESULT"] = (ll_rand(dice_sides) + 1); + LLNotificationsUtil::add("ChatCommandDiceRoll", args); + return true; + } + else if (cmd == utf8str_tolower(sRegionMsgCommand)) // Region Message / Dialog + { + if (data.length() > cmd.length() + 1) + { + std::string notification_message = data.substr(cmd.length() + 1); + std::vector<std::string> strings(5, "-1"); + // [0] grid_x, unused here + // [1] grid_y, unused here + strings[2] = gAgentID.asString(); // [2] agent_id of sender + // [3] senter name + std::string name; + LLAgentUI::buildFullname(name); + strings[3] = name; + strings[4] = notification_message; // [4] message + LLRegionInfoModel::sendEstateOwnerMessage(gMessageSystem, "simulatormessage", LLFloaterRegionInfo::getLastInvoice(), strings); + return true; + } + } + else if (cmd == utf8str_tolower(sSetNearbyChatChannelCmd)) // Set nearby chat channel + { + S32 chan; + if (input >> chan) + { + gSavedSettings.setS32("AlchemyNearbyChatChannel", chan); + return true; + } + } + else if (cmd == utf8str_tolower(sTeleportToCam)) + { + gAgent.teleportViaLocation(gAgentCamera.getCameraPositionGlobal()); + return true; + } + else if (cmd == utf8str_tolower(sHoverHeight)) // Hover height + { + F32 height; + if (input >> height) + { + gSavedPerAccountSettings.set("AvatarHoverOffsetZ", + llclamp<F32>(height, MIN_HOVER_Z, MAX_HOVER_Z)); + return true; + } + } + else if (cmd == utf8str_tolower(sResyncAnimCommand)) // Resync Animations + { + for (S32 i = 0; i < gObjectList.getNumObjects(); i++) + { + LLViewerObject* object = gObjectList.getObject(i); + if (object && object->isAvatar()) + { + LLVOAvatar* avatarp = (LLVOAvatar*)object; + if (avatarp) + { + for (const std::pair<LLUUID, S32> playpair : avatarp->mPlayingAnimations) + { + avatarp->stopMotion(playpair.first, TRUE); + avatarp->startMotion(playpair.first); + } + } + } + } + return true; + } + //else if (cmd == utf8str_tolower(sAOCommand)) + //{ + // std::string subcmd; + // if (input >> subcmd) + // { + // if (subcmd == "on") + // { + // gSavedPerAccountSettings.setBOOL("UseAO", TRUE); + // return true; + // } + // else if (subcmd == "off") + // { + // gSavedPerAccountSettings.setBOOL("UseAO", FALSE); + // return true; + // } + // else if (subcmd == "sit") + // { + // auto ao_set = LLAOEngine::instance().getSetByName(LLAOEngine::instance().getCurrentSetName()); + // if (input >> subcmd) + // { + // if (subcmd == "on") + // { + // LLAOEngine::instance().setOverrideSits(ao_set, true); + + // } + // else if (subcmd == "off") + // { + // LLAOEngine::instance().setOverrideSits(ao_set, false); + // } + // } + // else + // { + // LLAOEngine::instance().setOverrideSits(ao_set, !ao_set->getSitOverride()); + // } + // return true; + // } + // } + //} + } + return false; +} diff --git a/indra/newview/alchatcommand.h b/indra/newview/alchatcommand.h new file mode 100644 index 00000000000..4400947f96b --- /dev/null +++ b/indra/newview/alchatcommand.h @@ -0,0 +1,30 @@ +/** + * @file alchatcommand.h + * @brief ALChatCommand header for chat input commands + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Copyright (C) 2013 Drake Arconis + * + * 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. + * $/LicenseInfo$ + */ + +#ifndef AL_ALCHATCOMMAND_H +#define AL_ALCHATCOMMAND_H + +#include "linden_common.h" + +namespace ALChatCommand +{ + bool parseCommand(std::string data); +}; + +#endif // AL_ALCHATCOMMAND_H diff --git a/indra/newview/app_settings/settings_alchemy.xml b/indra/newview/app_settings/settings_alchemy.xml index d8438d902f9..63d4535c470 100644 --- a/indra/newview/app_settings/settings_alchemy.xml +++ b/indra/newview/app_settings/settings_alchemy.xml @@ -200,5 +200,16 @@ <key>Value</key> <string>/tp2cam</string> </map> + <key>AlchemyRezUnderLandGroup</key> + <map> + <key>Comment</key> + <string>Allows the agent to rez objects under the target locations land group. Must be a member of the land group.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> </map> </llsd> diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp index e19f1bd15c5..764c2f1ead3 100644 --- a/indra/newview/llchatbar.cpp +++ b/indra/newview/llchatbar.cpp @@ -36,6 +36,7 @@ #include "message.h" #include "llfocusmgr.h" +#include "alchatcommand.h" #include "llagent.h" #include "llbutton.h" #include "llcombobox.h" @@ -385,7 +386,7 @@ void LLChatBar::sendChat( EChatType type ) utf8_revised_text = utf8str_trim(utf8_revised_text); - if (!utf8_revised_text.empty()) + if (!utf8_revised_text.empty() && !ALChatCommand::parseCommand(utf8_revised_text)) { // Chat with animation sendChatFromViewer(utf8_revised_text, type, gSavedSettings.getBOOL("PlayChatAnim")); @@ -659,7 +660,7 @@ void LLChatBar::onCommitGesture(LLUICtrl* ctrl) LLGestureMgr::instance().triggerAndReviseString(text, &revised_text); revised_text = utf8str_trim(revised_text); - if (!revised_text.empty()) + if (!revised_text.empty() && !ALChatCommand::parseCommand(revised_text)) { // Don't play nodding animation sendChatFromViewer(revised_text, CHAT_TYPE_NORMAL, FALSE); diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp index 269172f282a..c21679cc041 100644 --- a/indra/newview/llfloaterimnearbychat.cpp +++ b/indra/newview/llfloaterimnearbychat.cpp @@ -67,6 +67,7 @@ #include "llviewerchat.h" #include "lltranslate.h" #include "llautoreplace.h" +#include "alchatcommand.h" // [RLVa:KB] - Checked: 2010-02-27 (RLVa-1.2.0b) #include "rlvactions.h" #include "rlvcommon.h" @@ -619,7 +620,7 @@ void LLFloaterIMNearbyChat::sendChat( EChatType type ) type = processChatTypeTriggers(type, utf8_revised_text); - if (!utf8_revised_text.empty()) + if (!utf8_revised_text.empty() && !ALChatCommand::parseCommand(utf8_revised_text)) { // Chat with animation sendChatFromViewer(utf8_revised_text, type, gSavedSettings.getBOOL("PlayChatAnim")); diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp index d22c40922ad..d569963ebc7 100644 --- a/indra/newview/llgesturemgr.cpp +++ b/indra/newview/llgesturemgr.cpp @@ -55,6 +55,7 @@ #include "llfloaterimnearbychat.h" #include "llappearancemgr.h" #include "llgesturelistener.h" +#include "alchatcommand.h" // [RLVa:KB] - Checked: RLVa-2.0.0 #include "rlvactions.h" @@ -1010,8 +1011,11 @@ void LLGestureMgr::runStep(LLMultiGesture* gesture, LLGestureStep* step) const BOOL animate = FALSE; - (LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"))-> - sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate); + if(!chat_text.empty() && !ALChatCommand::parseCommand(chat_text)) + { + (LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"))-> + sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate); + } gesture->mCurrentStep++; break; diff --git a/indra/newview/llregioninfomodel.h b/indra/newview/llregioninfomodel.h index baeff82fefb..4bcffedfba6 100644 --- a/indra/newview/llregioninfomodel.h +++ b/indra/newview/llregioninfomodel.h @@ -87,7 +87,7 @@ class LLRegionInfoModel : public LLSingleton<LLRegionInfoModel> private: void reset(); - // *FIXME: Duplicated code from LLPanelRegionInfo +public: static void sendEstateOwnerMessage( LLMessageSystem* msg, const std::string& request, diff --git a/indra/newview/llviewergesture.cpp b/indra/newview/llviewergesture.cpp index f30279d1e92..1a57225ce87 100644 --- a/indra/newview/llviewergesture.cpp +++ b/indra/newview/llviewergesture.cpp @@ -43,6 +43,8 @@ #include "llagent.h" #include "llfloaterimnearbychat.h" +#include "alchatcommand.h" + // Globals LLViewerGestureList gGestureList; @@ -127,7 +129,7 @@ void LLViewerGesture::doTrigger( BOOL send_chat ) } } - if (send_chat && !mOutputString.empty()) + if (send_chat && !mOutputString.empty() && !ALChatCommand::parseCommand(mOutputString)) { // Don't play nodding animation, since that might not blend // with the gesture animation. -- GitLab