diff options
author | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
commit | 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch) | |
tree | adca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/newview/llgesturemgr.cpp | |
parent | README.txt (diff) | |
download | meta-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.cpp | 1086 |
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 | |||
56 | LLGestureManager gGestureManager; | ||
57 | |||
58 | // Longest time, in seconds, to wait for all animations to stop playing | ||
59 | const F32 MAX_WAIT_ANIM_SECS = 30.f; | ||
60 | |||
61 | |||
62 | // Lightweight constructor. | ||
63 | // init() does the heavy lifting. | ||
64 | LLGestureManager::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. | ||
73 | LLGestureManager::~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 | |||
86 | void 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. | ||
94 | void 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 | |||
110 | void 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 | |||
194 | struct 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. | ||
203 | void 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 | |||
247 | void 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 | |||
289 | void 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 | |||
375 | BOOL 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 | |||
382 | BOOL 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 | |||
393 | void 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 | |||
434 | void 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. | ||
453 | void 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. | ||
468 | BOOL 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 | |||
545 | BOOL 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 | |||
566 | S32 LLGestureManager::getPlayingCount() const | ||
567 | { | ||
568 | return mPlaying.size(); | ||
569 | } | ||
570 | |||
571 | |||
572 | struct 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 | |||
580 | void 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. | ||
623 | void 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 | |||
771 | void 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 | ||
856 | void 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 | |||
964 | void 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 | |||
1004 | void 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 | |||
1016 | void LLGestureManager::addObserver(LLGestureManagerObserver* observer) | ||
1017 | { | ||
1018 | mObservers.push_back(observer); | ||
1019 | } | ||
1020 | |||
1021 | void 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. | ||
1034 | void 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 | |||
1048 | BOOL 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 | |||
1079 | void 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 | } | ||