PageRenderTime 49ms CodeModel.GetById 12ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/llagentlistener.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 509 lines | 402 code | 53 blank | 54 comment | 43 complexity | a2a9dbe6dc8d4be2d94dbf4f50277ac9 MD5 | raw file
  1/**
  2 * @file   llagentlistener.cpp
  3 * @author Brad Kittenbrink
  4 * @date   2009-07-10
  5 * @brief  Implementation for llagentlistener.
  6 * 
  7 * $LicenseInfo:firstyear=2009&license=viewerlgpl$
  8 * Second Life Viewer Source Code
  9 * Copyright (C) 2010, Linden Research, Inc.
 10 * 
 11 * This library is free software; you can redistribute it and/or
 12 * modify it under the terms of the GNU Lesser General Public
 13 * License as published by the Free Software Foundation;
 14 * version 2.1 of the License only.
 15 * 
 16 * This library is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 19 * Lesser General Public License for more details.
 20 * 
 21 * You should have received a copy of the GNU Lesser General Public
 22 * License along with this library; if not, write to the Free Software
 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 24 * 
 25 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 26 * $/LicenseInfo$
 27 */
 28
 29#include "llviewerprecompiledheaders.h"
 30
 31#include "llagentlistener.h"
 32
 33#include "llagent.h"
 34#include "llvoavatar.h"
 35#include "llcommandhandler.h"
 36#include "llslurl.h"
 37#include "llurldispatcher.h"
 38#include "llviewerobject.h"
 39#include "llviewerobjectlist.h"
 40#include "llviewerregion.h"
 41#include "llsdutil.h"
 42#include "llsdutil_math.h"
 43#include "lltoolgrab.h"
 44#include "llhudeffectlookat.h"
 45#include "llagentcamera.h"
 46
 47LLAgentListener::LLAgentListener(LLAgent &agent)
 48  : LLEventAPI("LLAgent",
 49               "LLAgent listener to (e.g.) teleport, sit, stand, etc."),
 50    mAgent(agent)
 51{
 52    add("requestTeleport",
 53        "Teleport: [\"regionname\"], [\"x\"], [\"y\"], [\"z\"]\n"
 54        "If [\"skip_confirmation\"] is true, use LLURLDispatcher rather than LLCommandDispatcher.",
 55        &LLAgentListener::requestTeleport);
 56    add("requestSit",
 57		"[\"obj_uuid\"]: id of object to sit on, use this or [\"position\"] to indicate the sit target"
 58		"[\"position\"]: region position {x, y, z} where to find closest object to sit on",
 59        &LLAgentListener::requestSit);
 60    add("requestStand",
 61        "Ask to stand up",
 62        &LLAgentListener::requestStand);
 63    add("requestTouch",
 64		"[\"obj_uuid\"]: id of object to touch, use this or [\"position\"] to indicate the object to touch"
 65		"[\"position\"]: region position {x, y, z} where to find closest object to touch"
 66		"[\"face\"]: optional object face number to touch[Default: 0]",
 67        &LLAgentListener::requestTouch);
 68    add("resetAxes",
 69        "Set the agent to a fixed orientation (optionally specify [\"lookat\"] = array of [x, y, z])",
 70        &LLAgentListener::resetAxes);
 71    add("getAxes",
 72        "Obsolete - use getPosition instead\n"
 73        "Send information about the agent's orientation on [\"reply\"]:\n"
 74        "[\"euler\"]: map of {roll, pitch, yaw}\n"
 75        "[\"quat\"]:  array of [x, y, z, w] quaternion values",
 76        &LLAgentListener::getAxes,
 77        LLSDMap("reply", LLSD()));
 78    add("getPosition",
 79        "Send information about the agent's position and orientation on [\"reply\"]:\n"
 80        "[\"region\"]: array of region {x, y, z} position\n"
 81        "[\"global\"]: array of global {x, y, z} position\n"
 82        "[\"euler\"]: map of {roll, pitch, yaw}\n"
 83        "[\"quat\"]:  array of [x, y, z, w] quaternion values",
 84        &LLAgentListener::getPosition,
 85        LLSDMap("reply", LLSD()));
 86    add("startAutoPilot",
 87        "Start the autopilot system using the following parameters:\n"
 88        "[\"target_global\"]: array of target global {x, y, z} position\n"
 89        "[\"stop_distance\"]: target maxiumum distance from target [default: autopilot guess]\n"
 90        "[\"target_rotation\"]: array of [x, y, z, w] quaternion values [default: no target]\n"
 91        "[\"rotation_threshold\"]: target maximum angle from target facing rotation [default: 0.03 radians]\n"
 92        "[\"behavior_name\"]: name of the autopilot behavior [default: \"\"]"
 93        "[\"allow_flying\"]: allow flying during autopilot [default: True]",
 94        //"[\"callback_pump\"]: pump to send success/failure and callback data to [default: none]\n"
 95        //"[\"callback_data\"]: data to send back during a callback [default: none]",
 96        &LLAgentListener::startAutoPilot);
 97    add("getAutoPilot",
 98        "Send information about current state of the autopilot system to [\"reply\"]:\n"
 99        "[\"enabled\"]: boolean indicating whether or not autopilot is enabled\n"
100        "[\"target_global\"]: array of target global {x, y, z} position\n"
101        "[\"leader_id\"]: uuid of target autopilot is following\n"
102        "[\"stop_distance\"]: target maximum distance from target\n"
103        "[\"target_distance\"]: last known distance from target\n"
104        "[\"use_rotation\"]: boolean indicating if autopilot has a target facing rotation\n"
105        "[\"target_facing\"]: array of {x, y} target direction to face\n"
106        "[\"rotation_threshold\"]: target maximum angle from target facing rotation\n"
107        "[\"behavior_name\"]: name of the autopilot behavior",
108        &LLAgentListener::getAutoPilot,
109        LLSDMap("reply", LLSD()));
110    add("startFollowPilot",
111		"[\"leader_id\"]: uuid of target to follow using the autopilot system (optional with avatar_name)\n"
112		"[\"avatar_name\"]: avatar name to follow using the autopilot system (optional with leader_id)\n"
113        "[\"allow_flying\"]: allow flying during autopilot [default: True]\n"
114        "[\"stop_distance\"]: target maxiumum distance from target [default: autopilot guess]",
115        &LLAgentListener::startFollowPilot);
116    add("setAutoPilotTarget",
117        "Update target for currently running autopilot:\n"
118        "[\"target_global\"]: array of target global {x, y, z} position",
119        &LLAgentListener::setAutoPilotTarget);
120    add("stopAutoPilot",
121        "Stop the autopilot system:\n"
122        "[\"user_cancel\"] indicates whether or not to act as though user canceled autopilot [default: false]",
123        &LLAgentListener::stopAutoPilot);
124    add("lookAt",
125		"[\"type\"]: number to indicate the lookAt type, 0 to clear\n"
126		"[\"obj_uuid\"]: id of object to look at, use this or [\"position\"] to indicate the target\n"
127		"[\"position\"]: region position {x, y, z} where to find closest object or avatar to look at",
128        &LLAgentListener::lookAt);
129}
130
131void LLAgentListener::requestTeleport(LLSD const & event_data) const
132{
133    if(event_data["skip_confirmation"].asBoolean())
134    {
135        LLSD params(LLSD::emptyArray());
136        params.append(event_data["regionname"]);
137        params.append(event_data["x"]);
138        params.append(event_data["y"]);
139        params.append(event_data["z"]);
140        LLCommandDispatcher::dispatch("teleport", params, LLSD(), NULL, "clicked", true);
141        // *TODO - lookup other LLCommandHandlers for "agent", "classified", "event", "group", "floater", "parcel", "login", login_refresh", "balance", "chat"
142        // should we just compose LLCommandHandler and LLDispatchListener?
143    }
144    else
145    {
146        std::string url = LLSLURL(event_data["regionname"], 
147                                  LLVector3(event_data["x"].asReal(), 
148                                            event_data["y"].asReal(), 
149                                            event_data["z"].asReal())).getSLURLString();
150        LLURLDispatcher::dispatch(url, "clicked", NULL, false);
151    }
152}
153
154void LLAgentListener::requestSit(LLSD const & event_data) const
155{
156    //mAgent.getAvatarObject()->sitOnObject();
157    // shamelessly ripped from llviewermenu.cpp:handle_sit_or_stand()
158    // *TODO - find a permanent place to share this code properly.
159
160	LLViewerObject *object = NULL;
161	if (event_data.has("obj_uuid"))
162	{
163		object = gObjectList.findObject(event_data["obj_uuid"]);
164	}
165	else if (event_data.has("position"))
166	{
167		LLVector3 target_position = ll_vector3_from_sd(event_data["position"]);
168		object = findObjectClosestTo(target_position);
169	}
170
171    if (object && object->getPCode() == LL_PCODE_VOLUME)
172    {
173        gMessageSystem->newMessageFast(_PREHASH_AgentRequestSit);
174        gMessageSystem->nextBlockFast(_PREHASH_AgentData);
175        gMessageSystem->addUUIDFast(_PREHASH_AgentID, mAgent.getID());
176        gMessageSystem->addUUIDFast(_PREHASH_SessionID, mAgent.getSessionID());
177        gMessageSystem->nextBlockFast(_PREHASH_TargetObject);
178        gMessageSystem->addUUIDFast(_PREHASH_TargetID, object->mID);
179        gMessageSystem->addVector3Fast(_PREHASH_Offset, LLVector3(0,0,0));
180
181        object->getRegion()->sendReliableMessage();
182    }
183	else
184	{
185		llwarns << "LLAgent requestSit could not find the sit target: " 
186			<< event_data << llendl;
187	}
188}
189
190void LLAgentListener::requestStand(LLSD const & event_data) const
191{
192    mAgent.setControlFlags(AGENT_CONTROL_STAND_UP);
193}
194
195
196LLViewerObject * LLAgentListener::findObjectClosestTo( const LLVector3 & position ) const
197{
198	LLViewerObject *object = NULL;
199
200	// Find the object closest to that position
201	F32 min_distance = 10000.0f;		// Start big
202	S32 num_objects = gObjectList.getNumObjects();
203	S32 cur_index = 0;
204	while (cur_index < num_objects)
205	{
206		LLViewerObject * cur_object = gObjectList.getObject(cur_index++);
207		if (cur_object)
208		{	// Calculate distance from the target position
209			LLVector3 target_diff = cur_object->getPositionRegion() - position;
210			F32 distance_to_target = target_diff.length();
211			if (distance_to_target < min_distance)
212			{	// Found an object closer
213				min_distance = distance_to_target;
214				object = cur_object;
215			}
216		}
217	}
218
219	return object;
220}
221
222
223void LLAgentListener::requestTouch(LLSD const & event_data) const
224{
225	LLViewerObject *object = NULL;
226	
227	if (event_data.has("obj_uuid"))
228	{
229		object = gObjectList.findObject(event_data["obj_uuid"]);
230	}
231	else if (event_data.has("position"))
232	{
233		LLVector3 target_position = ll_vector3_from_sd(event_data["position"]);
234		object = findObjectClosestTo(target_position);
235	}
236
237	S32 face = 0;
238	if (event_data.has("face"))
239	{
240		face = event_data["face"].asInteger();
241	}
242
243    if (object && object->getPCode() == LL_PCODE_VOLUME)
244    {
245		// Fake enough pick info to get it to (hopefully) work
246		LLPickInfo pick;
247		pick.mObjectFace = face;
248
249		/*
250		These values are sent to the simulator, but face seems to be easiest to use
251
252		pick.mUVCoords	 "UVCoord"
253		pick.mSTCoords	"STCoord"	
254		pick.mObjectFace	"FaceIndex"
255		pick.mIntersection	"Position"
256		pick.mNormal	"Normal"
257		pick.mBinormal	"Binormal"
258		*/
259
260		// A touch is a sketchy message sequence ... send a grab, immediately
261		// followed by un-grabbing, crossing fingers and hoping packets arrive in
262		// the correct order
263		send_ObjectGrab_message(object, pick, LLVector3::zero);
264		send_ObjectDeGrab_message(object, pick);
265    }
266	else
267	{
268		llwarns << "LLAgent requestTouch could not find the touch target " 
269			<< event_data["obj_uuid"].asUUID() << llendl;
270	}
271}
272
273
274void LLAgentListener::resetAxes(const LLSD& event_data) const
275{
276    if (event_data.has("lookat"))
277    {
278        mAgent.resetAxes(ll_vector3_from_sd(event_data["lookat"]));
279    }
280    else
281    {
282        // no "lookat", default call
283        mAgent.resetAxes();
284    }
285}
286
287void LLAgentListener::getAxes(const LLSD& event_data) const
288{
289    LLQuaternion quat(mAgent.getQuat());
290    F32 roll, pitch, yaw;
291    quat.getEulerAngles(&roll, &pitch, &yaw);
292    // The official query API for LLQuaternion's [x, y, z, w] values is its
293    // public member mQ...
294	LLSD reply = LLSD::emptyMap();
295	reply["quat"] = llsd_copy_array(boost::begin(quat.mQ), boost::end(quat.mQ));
296	reply["euler"] = LLSD::emptyMap();
297	reply["euler"]["roll"] = roll;
298	reply["euler"]["pitch"] = pitch;
299	reply["euler"]["yaw"] = yaw;
300    sendReply(reply, event_data);
301}
302
303void LLAgentListener::getPosition(const LLSD& event_data) const
304{
305    F32 roll, pitch, yaw;
306    LLQuaternion quat(mAgent.getQuat());
307    quat.getEulerAngles(&roll, &pitch, &yaw);
308
309	LLSD reply = LLSD::emptyMap();
310	reply["quat"] = llsd_copy_array(boost::begin(quat.mQ), boost::end(quat.mQ));
311	reply["euler"] = LLSD::emptyMap();
312	reply["euler"]["roll"] = roll;
313	reply["euler"]["pitch"] = pitch;
314	reply["euler"]["yaw"] = yaw;
315    reply["region"] = ll_sd_from_vector3(mAgent.getPositionAgent());
316    reply["global"] = ll_sd_from_vector3d(mAgent.getPositionGlobal());
317
318	sendReply(reply, event_data);
319}
320
321
322void LLAgentListener::startAutoPilot(LLSD const & event_data)
323{
324    LLQuaternion target_rotation_value;
325    LLQuaternion* target_rotation = NULL;
326    if (event_data.has("target_rotation"))
327    {
328        target_rotation_value = ll_quaternion_from_sd(event_data["target_rotation"]);
329        target_rotation = &target_rotation_value;
330    }
331    // *TODO: Use callback_pump and callback_data
332    F32 rotation_threshold = 0.03f;
333    if (event_data.has("rotation_threshold"))
334    {
335        rotation_threshold = event_data["rotation_threshold"].asReal();
336    }
337	
338	BOOL allow_flying = TRUE;
339	if (event_data.has("allow_flying"))
340	{
341		allow_flying = (BOOL) event_data["allow_flying"].asBoolean();
342		mAgent.setFlying(allow_flying);
343	}
344
345	F32 stop_distance = 0.f;
346	if (event_data.has("stop_distance"))
347	{
348		stop_distance = event_data["stop_distance"].asReal();
349	}
350
351	// Clear follow target, this is doing a path
352	mFollowTarget.setNull();
353
354    mAgent.startAutoPilotGlobal(ll_vector3d_from_sd(event_data["target_global"]),
355                                event_data["behavior_name"],
356                                target_rotation,
357                                NULL, NULL,
358                                stop_distance,
359                                rotation_threshold,
360								allow_flying);
361}
362
363void LLAgentListener::getAutoPilot(const LLSD& event_data) const
364{
365	LLSD reply = LLSD::emptyMap();
366	
367	LLSD::Boolean enabled = mAgent.getAutoPilot();
368	reply["enabled"] = enabled;
369	
370	reply["target_global"] = ll_sd_from_vector3d(mAgent.getAutoPilotTargetGlobal());
371	
372	reply["leader_id"] = mAgent.getAutoPilotLeaderID();
373	
374	reply["stop_distance"] = mAgent.getAutoPilotStopDistance();
375
376	reply["target_distance"] = mAgent.getAutoPilotTargetDist();
377	if (!enabled &&
378		mFollowTarget.notNull())
379	{	// Get an actual distance from the target object we were following
380		LLViewerObject * target = gObjectList.findObject(mFollowTarget);
381		if (target)
382		{	// Found the target AV, return the actual distance to them as well as their ID
383			LLVector3 difference = target->getPositionRegion() - mAgent.getPositionAgent();
384			reply["target_distance"] = difference.length();
385			reply["leader_id"] = mFollowTarget;
386		}
387	}
388
389	reply["use_rotation"] = (LLSD::Boolean) mAgent.getAutoPilotUseRotation();
390	reply["target_facing"] = ll_sd_from_vector3(mAgent.getAutoPilotTargetFacing());
391	reply["rotation_threshold"] = mAgent.getAutoPilotRotationThreshold();
392	reply["behavior_name"] = mAgent.getAutoPilotBehaviorName();
393	reply["fly"] = (LLSD::Boolean) mAgent.getFlying();
394
395	sendReply(reply, event_data);
396}
397
398void LLAgentListener::startFollowPilot(LLSD const & event_data)
399{
400	LLUUID target_id;
401
402	BOOL allow_flying = TRUE;
403	if (event_data.has("allow_flying"))
404	{
405		allow_flying = (BOOL) event_data["allow_flying"].asBoolean();
406	}
407
408	if (event_data.has("leader_id"))
409	{
410		target_id = event_data["leader_id"];
411	}
412	else if (event_data.has("avatar_name"))
413	{	// Find the avatar with matching name
414		std::string target_name = event_data["avatar_name"].asString();
415
416		if (target_name.length() > 0)
417		{
418			S32 num_objects = gObjectList.getNumObjects();
419			S32 cur_index = 0;
420			while (cur_index < num_objects)
421			{
422				LLViewerObject * cur_object = gObjectList.getObject(cur_index++);
423				if (cur_object &&
424					cur_object->asAvatar() &&
425					cur_object->asAvatar()->getFullname() == target_name)
426				{	// Found avatar with matching name, extract id and break out of loop
427					target_id = cur_object->getID();
428					break;
429				}
430			}
431		}
432	}
433
434	F32 stop_distance = 0.f;
435	if (event_data.has("stop_distance"))
436	{
437		stop_distance = event_data["stop_distance"].asReal();
438	}
439
440	if (target_id.notNull())
441	{
442		mAgent.setFlying(allow_flying);
443		mFollowTarget = target_id;	// Save follow target so we can report distance later
444
445	    mAgent.startFollowPilot(target_id, allow_flying, stop_distance);
446	}
447}
448
449void LLAgentListener::setAutoPilotTarget(LLSD const & event_data) const
450{
451	if (event_data.has("target_global"))
452	{
453		LLVector3d target_global(ll_vector3d_from_sd(event_data["target_global"]));
454		mAgent.setAutoPilotTargetGlobal(target_global);
455	}
456}
457
458void LLAgentListener::stopAutoPilot(LLSD const & event_data) const
459{
460	BOOL user_cancel = FALSE;
461	if (event_data.has("user_cancel"))
462	{
463		user_cancel = event_data["user_cancel"].asBoolean();
464	}
465    mAgent.stopAutoPilot(user_cancel);
466}
467
468void LLAgentListener::lookAt(LLSD const & event_data) const
469{
470	LLViewerObject *object = NULL;
471	if (event_data.has("obj_uuid"))
472	{
473		object = gObjectList.findObject(event_data["obj_uuid"]);
474	}
475	else if (event_data.has("position"))
476	{
477		LLVector3 target_position = ll_vector3_from_sd(event_data["position"]);
478		object = findObjectClosestTo(target_position);
479	}
480
481	S32 look_at_type = (S32) LOOKAT_TARGET_NONE;
482	if (event_data.has("type"))
483	{
484		look_at_type = event_data["type"].asInteger();
485	}
486	if (look_at_type >= (S32) LOOKAT_TARGET_NONE &&
487		look_at_type < (S32) LOOKAT_NUM_TARGETS)
488	{
489		gAgentCamera.setLookAt((ELookAtType) look_at_type, object);
490	}
491}
492
493void LLAgentListener::getGroups(const LLSD& event) const
494{
495    LLSD reply(LLSD::emptyArray());
496    for (LLDynamicArray<LLGroupData>::const_iterator
497             gi(mAgent.mGroups.begin()), gend(mAgent.mGroups.end());
498         gi != gend; ++gi)
499    {
500        reply.append(LLSDMap
501                     ("id", gi->mID)
502                     ("name", gi->mName)
503                     ("insignia", gi->mInsigniaID)
504                     ("notices", bool(gi->mAcceptNotices))
505                     ("display", bool(gi->mListInProfile))
506                     ("contrib", gi->mContribution));
507    }
508    sendReply(LLSDMap("groups", reply), event);
509}