aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llgesturemgr.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/newview/llgesturemgr.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/newview/llgesturemgr.cpp')
-rw-r--r--linden/indra/newview/llgesturemgr.cpp1086
1 files changed, 1086 insertions, 0 deletions
diff --git a/linden/indra/newview/llgesturemgr.cpp b/linden/indra/newview/llgesturemgr.cpp
new file mode 100644
index 0000000..8f906d9
--- /dev/null
+++ b/linden/indra/newview/llgesturemgr.cpp
@@ -0,0 +1,1086 @@
1/**
2 * @file llgesturemgr.cpp
3 * @brief Manager for playing gestures on the viewer
4 *
5 * Copyright (c) 2004-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "llviewerprecompiledheaders.h"
29
30#include "llgesturemgr.h"
31
32// system
33#include <functional>
34#include <algorithm>
35#include <boost/tokenizer.hpp>
36
37// library
38#include "lldatapacker.h"
39#include "llinventory.h"
40#include "llmultigesture.h"
41#include "llstl.h"
42#include "llstring.h" // todo: remove
43#include "llvfile.h"
44#include "message.h"
45
46// newview
47#include "llagent.h"
48#include "llchatbar.h"
49#include "llinventorymodel.h"
50#include "llnotify.h"
51#include "llviewermessage.h"
52#include "llvoavatar.h"
53#include "llviewerstats.h"
54#include "viewer.h"
55
56LLGestureManager gGestureManager;
57
58// Longest time, in seconds, to wait for all animations to stop playing
59const F32 MAX_WAIT_ANIM_SECS = 30.f;
60
61
62// Lightweight constructor.
63// init() does the heavy lifting.
64LLGestureManager::LLGestureManager()
65: mValid(FALSE),
66 mPlaying(),
67 mActive(),
68 mLoadingCount(0)
69{ }
70
71
72// We own the data for gestures, so clean them up.
73LLGestureManager::~LLGestureManager()
74{
75 item_map_t::iterator it;
76 for (it = mActive.begin(); it != mActive.end(); ++it)
77 {
78 LLMultiGesture* gesture = (*it).second;
79
80 delete gesture;
81 gesture = NULL;
82 }
83}
84
85
86void LLGestureManager::init()
87{
88 // TODO
89}
90
91
92// Use this version when you have the item_id but not the asset_id,
93// and you KNOW the inventory is loaded.
94void LLGestureManager::activateGesture(const LLUUID& item_id)
95{
96 LLViewerInventoryItem* item = gInventory.getItem(item_id);
97 if (!item) return;
98
99 LLUUID asset_id = item->getAssetUUID();
100
101 mLoadingCount = 1;
102 mDeactivateSimilarNames.clear();
103
104 const BOOL inform_server = TRUE;
105 const BOOL deactivate_similar = TRUE;
106 activateGestureWithAsset(item_id, asset_id, inform_server, deactivate_similar);
107}
108
109
110void LLGestureManager::activateGestures(LLViewerInventoryItem::item_array_t& items)
111{
112 // Load up the assets
113 S32 count = 0;
114 LLViewerInventoryItem::item_array_t::const_iterator it;
115 for (it = items.begin(); it != items.end(); ++it)
116 {
117 LLViewerInventoryItem* item = *it;
118
119 if (isGestureActive(item->getUUID()))
120 {
121 continue;
122 }
123 else
124 { // Make gesture active and persistent through login sessions. -spatters 07-12-06
125 activateGesture(item->getUUID());
126 }
127
128 count++;
129 }
130
131 mLoadingCount = count;
132 mDeactivateSimilarNames.clear();
133
134 for (it = items.begin(); it != items.end(); ++it)
135 {
136 LLViewerInventoryItem* item = *it;
137
138 if (isGestureActive(item->getUUID()))
139 {
140 continue;
141 }
142
143 // Don't inform server, we'll do that in bulk
144 const BOOL no_inform_server = FALSE;
145 const BOOL deactivate_similar = TRUE;
146 activateGestureWithAsset(item->getUUID(), item->getAssetUUID(),
147 no_inform_server,
148 deactivate_similar);
149 }
150
151 // Inform the database of this change
152 LLMessageSystem* msg = gMessageSystem;
153
154 BOOL start_message = TRUE;
155
156 for (it = items.begin(); it != items.end(); ++it)
157 {
158 LLViewerInventoryItem* item = *it;
159
160 if (isGestureActive(item->getUUID()))
161 {
162 continue;
163 }
164
165 if (start_message)
166 {
167 msg->newMessage("ActivateGestures");
168 msg->nextBlock("AgentData");
169 msg->addUUID("AgentID", gAgent.getID());
170 msg->addUUID("SessionID", gAgent.getSessionID());
171 msg->addU32("Flags", 0x0);
172 start_message = FALSE;
173 }
174
175 msg->nextBlock("Data");
176 msg->addUUID("ItemID", item->getUUID());
177 msg->addUUID("AssetID", item->getAssetUUID());
178 msg->addU32("GestureFlags", 0x0);
179
180 if (msg->getCurrentSendTotal() > MTUBYTES)
181 {
182 gAgent.sendReliableMessage();
183 start_message = TRUE;
184 }
185 }
186
187 if (!start_message)
188 {
189 gAgent.sendReliableMessage();
190 }
191}
192
193
194struct LLLoadInfo
195{
196 LLUUID mItemID;
197 BOOL mInformServer;
198 BOOL mDeactivateSimilar;
199};
200
201// If inform_server is true, will send a message upstream to update
202// the user_gesture_active table.
203void LLGestureManager::activateGestureWithAsset(const LLUUID& item_id,
204 const LLUUID& asset_id,
205 BOOL inform_server,
206 BOOL deactivate_similar)
207{
208 // If gesture is already active, nothing to do.
209 if (isGestureActive(item_id))
210 {
211 llwarns << "Tried to loadGesture twice " << item_id << llendl;
212 return;
213 }
214
215// if (asset_id.isNull())
216// {
217// llwarns << "loadGesture() - gesture has no asset" << llendl;
218// return;
219// }
220
221 // For now, put NULL into the item map. We'll build a gesture
222 // class object when the asset data arrives.
223 mActive[item_id] = NULL;
224
225 // Copy the UUID
226 if (asset_id.notNull())
227 {
228 LLLoadInfo* info = new LLLoadInfo;
229 info->mItemID = item_id;
230 info->mInformServer = inform_server;
231 info->mDeactivateSimilar = deactivate_similar;
232
233 const BOOL high_priority = TRUE;
234 gAssetStorage->getAssetData(asset_id,
235 LLAssetType::AT_GESTURE,
236 onLoadComplete,
237 (void*)info,
238 high_priority);
239 }
240 else
241 {
242 notifyObservers();
243 }
244}
245
246
247void LLGestureManager::deactivateGesture(const LLUUID& item_id)
248{
249 item_map_t::iterator it = mActive.find(item_id);
250 if (it == mActive.end())
251 {
252 llwarns << "deactivateGesture for inactive gesture " << item_id << llendl;
253 return;
254 }
255
256 // mActive owns this gesture pointer, so clean up memory.
257 LLMultiGesture* gesture = (*it).second;
258
259 // Can be NULL gestures in the map
260 if (gesture)
261 {
262 stopGesture(gesture);
263
264 delete gesture;
265 gesture = NULL;
266 }
267
268 mActive.erase(it);
269 gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
270
271 // Inform the database of this change
272 LLMessageSystem* msg = gMessageSystem;
273 msg->newMessage("DeactivateGestures");
274 msg->nextBlock("AgentData");
275 msg->addUUID("AgentID", gAgent.getID());
276 msg->addUUID("SessionID", gAgent.getSessionID());
277 msg->addU32("Flags", 0x0);
278
279 msg->nextBlock("Data");
280 msg->addUUID("ItemID", item_id);
281 msg->addU32("GestureFlags", 0x0);
282
283 gAgent.sendReliableMessage();
284
285 notifyObservers();
286}
287
288
289void LLGestureManager::deactivateSimilarGestures(LLMultiGesture* in, const LLUUID& in_item_id)
290{
291 std::vector<LLUUID> gest_item_ids;
292
293 // Deactivate all gestures that match
294 item_map_t::iterator it;
295 for (it = mActive.begin(); it != mActive.end(); )
296 {
297 const LLUUID& item_id = (*it).first;
298 LLMultiGesture* gest = (*it).second;
299
300 // Don't deactivate the gesture we are looking for duplicates of
301 // (for replaceGesture)
302 if (!gest || item_id == in_item_id)
303 {
304 // legal, can have null pointers in list
305 ++it;
306 }
307 else if ((!gest->mTrigger.empty() && gest->mTrigger == in->mTrigger)
308 || (gest->mKey != KEY_NONE && gest->mKey == in->mKey && gest->mMask == in->mMask))
309 {
310 gest_item_ids.push_back(item_id);
311
312 stopGesture(gest);
313
314 delete gest;
315 gest = NULL;
316
317 mActive.erase(it++);
318 gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
319
320 }
321 else
322 {
323 ++it;
324 }
325 }
326
327 // Inform database of the change
328 LLMessageSystem* msg = gMessageSystem;
329 BOOL start_message = TRUE;
330 std::vector<LLUUID>::const_iterator vit = gest_item_ids.begin();
331 while (vit != gest_item_ids.end())
332 {
333 if (start_message)
334 {
335 msg->newMessage("DeactivateGestures");
336 msg->nextBlock("AgentData");
337 msg->addUUID("AgentID", gAgent.getID());
338 msg->addUUID("SessionID", gAgent.getSessionID());
339 msg->addU32("Flags", 0x0);
340 start_message = FALSE;
341 }
342
343 msg->nextBlock("Data");
344 msg->addUUID("ItemID", *vit);
345 msg->addU32("GestureFlags", 0x0);
346
347 if (msg->getCurrentSendTotal() > MTUBYTES)
348 {
349 gAgent.sendReliableMessage();
350 start_message = TRUE;
351 }
352
353 ++vit;
354 }
355
356 if (!start_message)
357 {
358 gAgent.sendReliableMessage();
359 }
360
361 // Add to the list of names for the user.
362 for (vit = gest_item_ids.begin(); vit != gest_item_ids.end(); ++vit)
363 {
364 LLViewerInventoryItem* item = gInventory.getItem(*vit);
365 if (!item) continue;
366
367 mDeactivateSimilarNames.append(item->getName());
368 mDeactivateSimilarNames.append("\n");
369 }
370
371 notifyObservers();
372}
373
374
375BOOL LLGestureManager::isGestureActive(const LLUUID& item_id)
376{
377 item_map_t::iterator it = mActive.find(item_id);
378 return (it != mActive.end());
379}
380
381
382BOOL LLGestureManager::isGesturePlaying(const LLUUID& item_id)
383{
384 item_map_t::iterator it = mActive.find(item_id);
385 if (it == mActive.end()) return FALSE;
386
387 LLMultiGesture* gesture = (*it).second;
388 if (!gesture) return FALSE;
389
390 return gesture->mPlaying;
391}
392
393void LLGestureManager::replaceGesture(const LLUUID& item_id, LLMultiGesture* new_gesture, const LLUUID& asset_id)
394{
395 item_map_t::iterator it = mActive.find(item_id);
396 if (it == mActive.end())
397 {
398 llwarns << "replaceGesture for inactive gesture " << item_id << llendl;
399 return;
400 }
401
402 LLMultiGesture* old_gesture = (*it).second;
403 stopGesture(old_gesture);
404
405 mActive.erase(item_id);
406
407 mActive[item_id] = new_gesture;
408
409 delete old_gesture;
410 old_gesture = NULL;
411
412 if (asset_id.notNull())
413 {
414 mLoadingCount = 1;
415 mDeactivateSimilarNames.clear();
416
417 LLLoadInfo* info = new LLLoadInfo;
418 info->mItemID = item_id;
419 info->mInformServer = TRUE;
420 info->mDeactivateSimilar = TRUE;
421
422 const BOOL high_priority = TRUE;
423 gAssetStorage->getAssetData(asset_id,
424 LLAssetType::AT_GESTURE,
425 onLoadComplete,
426 (void*)info,
427 high_priority);
428 }
429
430 notifyObservers();
431}
432
433
434void LLGestureManager::playGesture(LLMultiGesture* gesture)
435{
436 if (!gesture) return;
437
438 // Reset gesture to first step
439 gesture->mCurrentStep = 0;
440
441 // Add to list of playing
442 gesture->mPlaying = TRUE;
443 mPlaying.push_back(gesture);
444
445 // And get it going
446 stepGesture(gesture);
447
448 notifyObservers();
449}
450
451
452// Convenience function that looks up the item_id for you.
453void LLGestureManager::playGesture(const LLUUID& item_id)
454{
455 item_map_t::iterator it = mActive.find(item_id);
456 if (it == mActive.end()) return;
457
458 LLMultiGesture* gesture = (*it).second;
459 if (!gesture) return;
460
461 playGesture(gesture);
462}
463
464
465// Iterates through space delimited tokens in string, triggering any gestures found.
466// Generates a revised string that has the found tokens replaced by their replacement strings
467// and (as a minor side effect) has multiple spaces in a row replaced by single spaces.
468BOOL LLGestureManager::triggerAndReviseString(const std::string &utf8str, std::string* revised_string)
469{
470 LLString tokenized = LLString(utf8str.c_str());
471
472 BOOL found_gestures = FALSE;
473 BOOL first_token = TRUE;
474
475 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
476 boost::char_separator<char> sep(" ");
477 tokenizer tokens(tokenized, sep);
478 tokenizer::iterator token_iter;
479
480 for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
481 {
482 const char* cur_token = token_iter->c_str();
483 LLMultiGesture* gesture = NULL;
484
485 // Only pay attention to the first gesture in the string.
486 if( !found_gestures )
487 {
488 LLString cur_token_lower = cur_token;
489 LLString::toLower(cur_token_lower);
490
491 item_map_t::iterator it;
492 for (it = mActive.begin(); it != mActive.end(); ++it)
493 {
494 gesture = (*it).second;
495
496 // Gesture asset data might not have arrived yet
497 if (!gesture) continue;
498
499 if (!stricmp(gesture->mTrigger.c_str(), cur_token_lower.c_str()))
500 {
501 playGesture(gesture);
502
503 if (!gesture->mReplaceText.empty())
504 {
505 if( !first_token )
506 {
507 revised_string->append( " " );
508 }
509
510 // Don't muck with the user's capitalization if we don't have to.
511 LLString output = gesture->mReplaceText.c_str();
512 LLString output_lower = output;
513 LLString::toLower(output_lower);
514 if( cur_token_lower == output_lower )
515 {
516 revised_string->append( cur_token );
517 }
518 else
519 {
520 revised_string->append( output );
521 }
522 }
523 found_gestures = TRUE;
524 break;
525 }
526 gesture = NULL;
527 }
528 }
529
530 if( !gesture )
531 {
532 if( !first_token )
533 {
534 revised_string->append( " " );
535 }
536 revised_string->append( cur_token );
537 }
538
539 first_token = FALSE;
540 }
541 return found_gestures;
542}
543
544
545BOOL LLGestureManager::triggerGesture(KEY key, MASK mask)
546{
547 item_map_t::iterator it;
548 for (it = mActive.begin(); it != mActive.end(); ++it)
549 {
550 LLMultiGesture* gesture = (*it).second;
551
552 // asset data might not have arrived yet
553 if (!gesture) continue;
554
555 if (gesture->mKey == key
556 && gesture->mMask == mask)
557 {
558 playGesture(gesture);
559 return TRUE;
560 }
561 }
562 return FALSE;
563}
564
565
566S32 LLGestureManager::getPlayingCount() const
567{
568 return mPlaying.size();
569}
570
571
572struct IsGesturePlaying : public std::unary_function<LLMultiGesture*, bool>
573{
574 bool operator()(const LLMultiGesture* gesture) const
575 {
576 return gesture->mPlaying ? true : false;
577 }
578};
579
580void LLGestureManager::update()
581{
582 S32 i;
583 for (i = 0; i < (S32)mPlaying.size(); ++i)
584 {
585 stepGesture(mPlaying[i]);
586 }
587
588 // Clear out gestures that are done, by moving all the
589 // ones that are still playing to the front.
590 std::vector<LLMultiGesture*>::iterator new_end;
591 new_end = std::partition(mPlaying.begin(),
592 mPlaying.end(),
593 IsGesturePlaying());
594
595 // Something finished playing
596 if (new_end != mPlaying.end())
597 {
598 // Delete the completed gestures that want deletion
599 std::vector<LLMultiGesture*>::iterator it;
600 for (it = new_end; it != mPlaying.end(); ++it)
601 {
602 LLMultiGesture* gesture = *it;
603
604 if (gesture->mDoneCallback)
605 {
606 gesture->mDoneCallback(gesture, gesture->mCallbackData);
607
608 // callback might have deleted gesture, can't
609 // rely on this pointer any more
610 gesture = NULL;
611 }
612 }
613
614 // And take done gestures out of the playing list
615 mPlaying.erase(new_end, mPlaying.end());
616
617 notifyObservers();
618 }
619}
620
621
622// Run all steps until you're either done or hit a wait.
623void LLGestureManager::stepGesture(LLMultiGesture* gesture)
624{
625 if (!gesture)
626 {
627 return;
628 }
629 LLVOAvatar* avatar = gAgent.getAvatarObject();
630 if (!avatar) return;
631
632 // Of the ones that started playing, have any stopped?
633
634 std::set<LLUUID>::iterator gest_it;
635 for (gest_it = gesture->mPlayingAnimIDs.begin();
636 gest_it != gesture->mPlayingAnimIDs.end();
637 )
638 {
639 // look in signaled animations (simulator's view of what is
640 // currently playing.
641 LLVOAvatar::AnimIterator play_it = avatar->mSignaledAnimations.find(*gest_it);
642 if (play_it != avatar->mSignaledAnimations.end())
643 {
644 ++gest_it;
645 }
646 else
647 {
648 // not found, so not currently playing or scheduled to play
649 // delete from the triggered set
650 gesture->mPlayingAnimIDs.erase(gest_it++);
651 }
652 }
653
654 // Of all the animations that we asked the sim to start for us,
655 // pick up the ones that have actually started.
656 for (gest_it = gesture->mRequestedAnimIDs.begin();
657 gest_it != gesture->mRequestedAnimIDs.end();
658 )
659 {
660 LLVOAvatar::AnimIterator play_it = avatar->mSignaledAnimations.find(*gest_it);
661 if (play_it != avatar->mSignaledAnimations.end())
662 {
663 // Hooray, this animation has started playing!
664 // Copy into playing.
665 gesture->mPlayingAnimIDs.insert(*gest_it);
666 gesture->mRequestedAnimIDs.erase(gest_it++);
667 }
668 else
669 {
670 // nope, not playing yet
671 ++gest_it;
672 }
673 }
674
675 // Run the current steps
676 BOOL waiting = FALSE;
677 while (!waiting && gesture->mPlaying)
678 {
679 // Get the current step, if there is one.
680 // Otherwise enter the waiting at end state.
681 LLGestureStep* step = NULL;
682 if (gesture->mCurrentStep < (S32)gesture->mSteps.size())
683 {
684 step = gesture->mSteps[gesture->mCurrentStep];
685 llassert(step != NULL);
686 }
687 else
688 {
689 // step stays null, we're off the end
690 gesture->mWaitingAtEnd = TRUE;
691 }
692
693
694 // If we're waiting at the end, wait for all gestures to stop
695 // playing.
696 // TODO: Wait for all sounds to complete as well.
697 if (gesture->mWaitingAtEnd)
698 {
699 // Neither do we have any pending requests, nor are they
700 // still playing.
701 if ((gesture->mRequestedAnimIDs.empty()
702 && gesture->mPlayingAnimIDs.empty()))
703 {
704 // all animations are done playing
705 gesture->mWaitingAtEnd = FALSE;
706 gesture->mPlaying = FALSE;
707 }
708 else
709 {
710 waiting = TRUE;
711 }
712 continue;
713 }
714
715 // If we're waiting on our animations to stop, poll for
716 // completion.
717 if (gesture->mWaitingAnimations)
718 {
719 // Neither do we have any pending requests, nor are they
720 // still playing.
721 if ((gesture->mRequestedAnimIDs.empty()
722 && gesture->mPlayingAnimIDs.empty()))
723 {
724 // all animations are done playing
725 gesture->mWaitingAnimations = FALSE;
726 gesture->mCurrentStep++;
727 }
728 else if (gesture->mWaitTimer.getElapsedTimeF32() > MAX_WAIT_ANIM_SECS)
729 {
730 // we've waited too long for an animation
731 llinfos << "Waited too long for animations to stop, continuing gesture."
732 << llendl;
733 gesture->mWaitingAnimations = FALSE;
734 gesture->mCurrentStep++;
735 }
736 else
737 {
738 waiting = TRUE;
739 }
740 continue;
741 }
742
743 // If we're waiting a fixed amount of time, check for timer
744 // expiration.
745 if (gesture->mWaitingTimer)
746 {
747 // We're waiting for a certain amount of time to pass
748 LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
749
750 F32 elapsed = gesture->mWaitTimer.getElapsedTimeF32();
751 if (elapsed > wait_step->mWaitSeconds)
752 {
753 // wait is done, continue execution
754 gesture->mWaitingTimer = FALSE;
755 gesture->mCurrentStep++;
756 }
757 else
758 {
759 // we're waiting, so execution is done for now
760 waiting = TRUE;
761 }
762 continue;
763 }
764
765 // Not waiting, do normal execution
766 runStep(gesture, step);
767 }
768}
769
770
771void LLGestureManager::runStep(LLMultiGesture* gesture, LLGestureStep* step)
772{
773 switch(step->getType())
774 {
775 case STEP_ANIMATION:
776 {
777 LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
778 if (anim_step->mAnimAssetID.isNull())
779 {
780 gesture->mCurrentStep++;
781 }
782
783 if (anim_step->mFlags & ANIM_FLAG_STOP)
784 {
785 gAgent.sendAnimationRequest(anim_step->mAnimAssetID, ANIM_REQUEST_STOP);
786 // remove it from our request set in case we just requested it
787 std::set<LLUUID>::iterator set_it = gesture->mRequestedAnimIDs.find(anim_step->mAnimAssetID);
788 if (set_it != gesture->mRequestedAnimIDs.end())
789 {
790 gesture->mRequestedAnimIDs.erase(set_it);
791 }
792 }
793 else
794 {
795 gAgent.sendAnimationRequest(anim_step->mAnimAssetID, ANIM_REQUEST_START);
796 // Indicate that we've requested this animation to play as
797 // part of this gesture (but it won't start playing for at
798 // least one round-trip to simulator).
799 gesture->mRequestedAnimIDs.insert(anim_step->mAnimAssetID);
800 }
801 gesture->mCurrentStep++;
802 break;
803 }
804 case STEP_SOUND:
805 {
806 LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
807 const LLUUID& sound_id = sound_step->mSoundAssetID;
808 const F32 volume = 1.f;
809 send_sound_trigger(sound_id, volume);
810 gesture->mCurrentStep++;
811 break;
812 }
813 case STEP_CHAT:
814 {
815 LLGestureStepChat* chat_step = (LLGestureStepChat*)step;
816 std::string chat_text = chat_step->mChatText;
817 // Don't animate the nodding, as this might not blend with
818 // other playing animations.
819 const BOOL animate = FALSE;
820
821 gChatBar->sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate);
822 gesture->mCurrentStep++;
823 break;
824 }
825 case STEP_WAIT:
826 {
827 LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
828 if (wait_step->mFlags & WAIT_FLAG_TIME)
829 {
830 gesture->mWaitingTimer = TRUE;
831 gesture->mWaitTimer.reset();
832 }
833 else if (wait_step->mFlags & WAIT_FLAG_ALL_ANIM)
834 {
835 gesture->mWaitingAnimations = TRUE;
836 // Use the wait timer as a deadlock breaker for animation
837 // waits.
838 gesture->mWaitTimer.reset();
839 }
840 else
841 {
842 gesture->mCurrentStep++;
843 }
844 // Don't increment instruction pointer until wait is complete.
845 break;
846 }
847 default:
848 {
849 break;
850 }
851 }
852}
853
854
855// static
856void LLGestureManager::onLoadComplete(LLVFS *vfs,
857 const LLUUID& asset_uuid,
858 LLAssetType::EType type,
859 void* user_data, S32 status)
860{
861 LLLoadInfo* info = (LLLoadInfo*)user_data;
862
863 LLUUID item_id = info->mItemID;
864 BOOL inform_server = info->mInformServer;
865 BOOL deactivate_similar = info->mDeactivateSimilar;
866
867 delete info;
868 info = NULL;
869
870 gGestureManager.mLoadingCount--;
871
872 if (0 == status)
873 {
874 LLVFile file(vfs, asset_uuid, type, LLVFile::READ);
875 S32 size = file.getSize();
876
877 char* buffer = new char[size+1];
878 file.read((U8*)buffer, size);
879 // ensure there's a trailing NULL so strlen will work.
880 buffer[size] = '\0';
881
882 LLMultiGesture* gesture = new LLMultiGesture();
883
884 LLDataPackerAsciiBuffer dp(buffer, size+1);
885 BOOL ok = gesture->deserialize(dp);
886
887 if (ok)
888 {
889 if (deactivate_similar)
890 {
891 gGestureManager.deactivateSimilarGestures(gesture, item_id);
892
893 // Display deactivation message if this was the last of the bunch.
894 if (gGestureManager.mLoadingCount == 0
895 && gGestureManager.mDeactivateSimilarNames.length() > 0)
896 {
897 // we're done with this set of deactivations
898 LLString::format_map_t args;
899 args["[NAMES]"] = gGestureManager.mDeactivateSimilarNames;
900 LLNotifyBox::showXml("DeactivatedGesturesTrigger", args);
901 }
902 }
903
904 // Everything has been successful. Add to the active list.
905 gGestureManager.mActive[item_id] = gesture;
906 gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
907 if (inform_server)
908 {
909 // Inform the database of this change
910 LLMessageSystem* msg = gMessageSystem;
911 msg->newMessage("ActivateGestures");
912 msg->nextBlock("AgentData");
913 msg->addUUID("AgentID", gAgent.getID());
914 msg->addUUID("SessionID", gAgent.getSessionID());
915 msg->addU32("Flags", 0x0);
916
917 msg->nextBlock("Data");
918 msg->addUUID("ItemID", item_id);
919 msg->addUUID("AssetID", asset_uuid);
920 msg->addU32("GestureFlags", 0x0);
921
922 gAgent.sendReliableMessage();
923 }
924
925 gGestureManager.notifyObservers();
926 }
927 else
928 {
929 llwarns << "Unable to load gesture" << llendl;
930
931 gGestureManager.mActive.erase(item_id);
932
933 delete gesture;
934 gesture = NULL;
935 }
936
937 delete [] buffer;
938 buffer = NULL;
939 }
940 else
941 {
942 if( gViewerStats )
943 {
944 gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
945 }
946
947 if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
948 LL_ERR_FILE_EMPTY == status)
949 {
950 LLNotifyBox::showXml("GestureMissing");
951 }
952 else
953 {
954 LLNotifyBox::showXml("UnableToLoadGesture");
955 }
956
957 llwarns << "Problem loading gesture: " << status << llendl;
958
959 gGestureManager.mActive.erase(item_id);
960 }
961}
962
963
964void LLGestureManager::stopGesture(LLMultiGesture* gesture)
965{
966 if (!gesture) return;
967
968 // Stop any animations that this gesture is currently playing
969 std::set<LLUUID>::const_iterator set_it;
970 for (set_it = gesture->mRequestedAnimIDs.begin(); set_it != gesture->mRequestedAnimIDs.end(); ++set_it)
971 {
972 const LLUUID& anim_id = *set_it;
973 gAgent.sendAnimationRequest(anim_id, ANIM_REQUEST_STOP);
974 }
975 for (set_it = gesture->mPlayingAnimIDs.begin(); set_it != gesture->mPlayingAnimIDs.end(); ++set_it)
976 {
977 const LLUUID& anim_id = *set_it;
978 gAgent.sendAnimationRequest(anim_id, ANIM_REQUEST_STOP);
979 }
980
981 std::vector<LLMultiGesture*>::iterator it;
982 it = std::find(mPlaying.begin(), mPlaying.end(), gesture);
983 while (it != mPlaying.end())
984 {
985 mPlaying.erase(it);
986 it = std::find(mPlaying.begin(), mPlaying.end(), gesture);
987 }
988
989 gesture->reset();
990
991 if (gesture->mDoneCallback)
992 {
993 gesture->mDoneCallback(gesture, gesture->mCallbackData);
994
995 // callback might have deleted gesture, can't
996 // rely on this pointer any more
997 gesture = NULL;
998 }
999
1000 notifyObservers();
1001}
1002
1003
1004void LLGestureManager::stopGesture(const LLUUID& item_id)
1005{
1006 item_map_t::iterator it = mActive.find(item_id);
1007 if (it == mActive.end()) return;
1008
1009 LLMultiGesture* gesture = (*it).second;
1010 if (!gesture) return;
1011
1012 stopGesture(gesture);
1013}
1014
1015
1016void LLGestureManager::addObserver(LLGestureManagerObserver* observer)
1017{
1018 mObservers.push_back(observer);
1019}
1020
1021void LLGestureManager::removeObserver(LLGestureManagerObserver* observer)
1022{
1023 std::vector<LLGestureManagerObserver*>::iterator it;
1024 it = std::find(mObservers.begin(), mObservers.end(), observer);
1025 if (it != mObservers.end())
1026 {
1027 mObservers.erase(it);
1028 }
1029}
1030
1031// Call this method when it's time to update everyone on a new state.
1032// Copy the list because an observer could respond by removing itself
1033// from the list.
1034void LLGestureManager::notifyObservers()
1035{
1036 lldebugs << "LLGestureManager::notifyObservers" << llendl;
1037
1038 std::vector<LLGestureManagerObserver*> observers = mObservers;
1039
1040 std::vector<LLGestureManagerObserver*>::iterator it;
1041 for (it = observers.begin(); it != observers.end(); ++it)
1042 {
1043 LLGestureManagerObserver* observer = *it;
1044 observer->changed();
1045 }
1046}
1047
1048BOOL LLGestureManager::matchPrefix(const std::string& in_str, std::string* out_str)
1049{
1050 S32 in_len = in_str.length();
1051
1052 item_map_t::iterator it;
1053 for (it = mActive.begin(); it != mActive.end(); ++it)
1054 {
1055 LLMultiGesture* gesture = (*it).second;
1056 if (gesture)
1057 {
1058 const std::string& trigger = gesture->getTrigger();
1059
1060 if (in_len > (S32)trigger.length())
1061 {
1062 // too short, bail out
1063 continue;
1064 }
1065
1066 LLString trigger_trunc = trigger;
1067 LLString::truncate(trigger_trunc, in_len);
1068 if (!LLString::compareInsensitive(in_str.c_str(), trigger_trunc.c_str()))
1069 {
1070 *out_str = trigger;
1071 return TRUE;
1072 }
1073 }
1074 }
1075 return FALSE;
1076}
1077
1078
1079void LLGestureManager::getItemIDs(std::vector<LLUUID>* ids)
1080{
1081 item_map_t::const_iterator it;
1082 for (it = mActive.begin(); it != mActive.end(); ++it)
1083 {
1084 ids->push_back(it->first);
1085 }
1086}