‪Black Ops 3 Source Code Explorer  0.1
‪An script explorer for Black Ops 3 by ZeRoY
face.gsc
Go to the documentation of this file.
1 #using scripts\shared\math_shared;
2 #using scripts\shared\util_shared;
3 
4 #insert scripts\shared\shared.gsh;
5 
6 #precache( "lui_menu", "TempDialog" );
7 #precache( "lui_menu_data", "dialogText" );
8 #precache( "lui_menu_data", "title" );
9 
10 // face.gsc
11 // Supposed to handle all facial and dialogue animations from regular scripts.
12 //#using_animtree ("generic_human"); - This file doesn't call animations directly.
13 
14 
15 function ‪SayGenericDialogue(typeString)
16 {
17 
18  if (level.disableGenericDialog)
19  {
20  return;
21  }
22 
23  switch (typeString)
24  {
25  case "attack":
26  importance = 0.5;
27  break;
28  case "swing":
29  importance = 0.5;
30  typeString = "attack";
31  break;
32  case "flashbang":
33  importance = 0.7;
34  break;
35  case "pain_small":
36  importance = 0.4;
37  break;
38  case "pain_bullet":
39  wait(.01); //make sure dialog system has had a chance to deal with death before we play another line.
40  importance = 0.4;
41  break;
42  default:
43  /#println ("Unexpected generic dialog string: "+typeString);#/
44  importance = 0.3;
45  break;
46  }
47 
48  ‪SayGenericDialogueWithImportance(typeString, importance);
49 
50 }
51 
52 // Makes a character say the specified line in his voice, if he's not already saying something more
53 // important.
54 function ‪SayGenericDialogueWithImportance(typeString, importance)
55 {
56  soundAlias = "dds_";
57 
58  if( isdefined( self.dds_characterID ) )
59  {
60  soundAlias += self.dds_characterID;
61  }
62  else
63  {
64  /#printLn( "this AI does not have a dds_characterID" );#/
65  return;
66  }
67 
68  soundAlias += "_" + typeString;
69 
70  if(SoundExists(soundAlias))
71  {
72  self thread ‪PlayFaceThread (undefined, soundAlias, importance);
73  }
74 }
75 
76 function ‪SetIdleFaceDelayed(facialAnimationArray)
77 {
78  self.a.idleFace = facialAnimationArray;
79 }
80 
81 // Sets the facial expression to return to when not saying dialogue.
82 // The array is animation1, weight1, animation2, weight2, etc. The animations will play in turn - each time
83 // one finishes a new one will be chosen randomly based on weight.
84 function ‪SetIdleFace(facialAnimationArray)
85 {
86  if (!anim.useFacialAnims)
87  {
88  return;
89  }
90 
91  self.a.idleFace = facialAnimationArray;
92  self ‪PlayIdleFace();
93 }
94 
95 // Makes the character play the specified sound and animation. The anim and the sound are optional - you
96 // can just defined one if you don't have both.
97 // Generally, importance should be in the range of 0.6-0.8 for scripted dialogue.
98 // Importance is a float, from 0 to 1.
99 // 0.0 - Idle expressions
100 // 0.1-0.5 - most generic dialogue
101 // 0.6-0.8 - most scripted dialogue
102 // 0.9 - pain
103 // 1.0 - death
104 // Importance can also be one of these strings: "any", "pain" or "death", which specfies what sounds can
105 // interrupt this one.
106 function ‪SaySpecificDialogue(facialanim, soundAlias, importance, notifyString, waitOrNot, timeToWait, toplayer)
107 {
109  self thread ‪PlayFaceThread(facialanim, soundAlias, importance, notifyString, waitOrNot, timeToWait, toplayer);
110 }
111 
112 // DEAD CODE REMOVAL
113 // Takes an array with a set of "anim" entries and a corresponding set of "weight" entries.
114 //ChooseAnimFromSet( animSet )
115 //{
116 // return; // Facial animations are now part of full body aniamtions
117 /*
118  if (!anim.useFacialAnims)
119  return;
120  // First, normalize the weights.
121  totalWeight = 0;
122  numAnims = animSet["anim"].size;
123  for ( i=0 ; i<numAnims ; i++ )
124  {
125  totalWeight += animSet["weight"][i];
126  }
127  for ( i=0 ; i<numAnims ; i++ )
128  {
129  animSet["weight"][i] = animSet["weight"][i] / totalWeight;
130  }
131 
132  // Now choose an animation.
133  rand = RandomFloat(1);
134  runningTotal = 0;
135  chosenAnim = undefined;
136  for ( i=0 ; i<numAnims ; i++ )
137  {
138  runningTotal += animSet["weight"][i];
139  if (runningTotal >= rand)
140  {
141  chosenAnim = i;
142  break;
143  }
144  }
145  assert(isdefined(chosenAnim), "Logic error in ChooseAnimFromSet. Rand is " + rand + ".");
146  return animSet["anim"][chosenAnim];
147 */
148 //}
149 
150 
151 
152 //-----------------------------------------------------
153 // Housekeeping functions - these are for internal use
154 //-----------------------------------------------------
155 
156 // PlayIdleFace doesn't force an idle animation to play - it will interrupt a current idle animation, but it
157 // won't play over a more important animation, like dialogue.
158 function ‪PlayIdleFace()
159 {
160  return; // Idle facial animations are now in the full-body animations.
161 }
162 
163 // PlayFaceThread is the workhorse of the system - it checks the importance, and if it's high enough, it
164 // plays the animation and/or sound specified.
165 // The waitOrNot parameter specifies what to do if another animation/sound is already playing.
166 // Options: "wait" or undefined. TimeToWait is an optional timeout time for waiting.
167 // Waiting faces are queued.
168 function ‪PlayFaceThread( facialanim, str_script_alias, importance, notifyString, waitOrNot, timeToWait, toplayer )
169 {
170  //NOTE most of the described functionality was removed in COD2 (!)
171  //Talk about misleading comments...
172  //This must be called in it's own thread.
173  //
174  //The behavior has been simplifed greatly
175  // lines never wait
176  // a line stops the current line if at equal or higher priority
177  // a line doesn't play if at lower priority
178  //SBM 8/3/08
179 
180  self endon( "death" );
181 
182  if ( !isdefined( str_script_alias ) )
183  {
184  wait 1;
185  self notify( notifyString );
186  return; //no sound, don't do anything facials are now included into the main anims
187  }
188 
189  str_notify_alias = str_script_alias; // Save this off because the script alias gets changed for females
190 
191  ‪DEFAULT( level.NumberOfImportantPeopleTalking, 0 );
192  ‪DEFAULT( level.TalkNotifySeed, 0 );
193  ‪DEFAULT( notifyString, "PlayFaceThread " + str_script_alias );
194  ‪DEFAULT( self.‪a, SpawnStruct() );
195  ‪DEFAULT( self.‪a.facialSoundDone, true );
196  ‪DEFAULT( self.isTalking, false );
197 
198  if ( self.isTalking ) //currently talking
199  {
200  //TUEY added equal to, so that the system won't spam the same notifies
201  if ( IsDefined( self.‪a.currentDialogImportance ) )
202  {
203  if ( importance < self.‪a.currentDialogImportance )
204  {
205  wait 1;
206  self notify( notifyString );
207  return; //skip it cause its not important
208  }
209  else if ( importance == self.‪a.currentDialogImportance )
210  {
211  if ( self.‪a.facialSoundAlias == str_script_alias )
212  {
213  return; //playing same alias twice? probably a script error somewhere
214  }
215 
216  /# println( "WARNING: delaying alias " + self.‪a.facialSoundAlias + " to play " + str_script_alias ); #/
217 
218  while ( self.isTalking ) //while is to fix a race condition
219  {
220  self waittill( "done speaking" ); //wait currently playing thread to finish
221  }
222  }
223  }
224  else
225  {
226  /# PrintLn("WARNING: interrupting alias "+self.‪a.facialSoundAlias+" to play "+str_script_alias); #/
227 
228  self StopSound( self.‪a.facialSoundAlias );
229  self notify("cancel speaking");
230 
231  while ( self.isTalking ) //while is to fix a race condition
232  {
233  self waittill( "done speaking" ); //wait currently playing thread to finish
234  }
235  }
236  }
237 
238  assert( self.‪a.facialSoundDone );
239  assert( self.‪a.facialSoundAlias == undefined );
240  assert( self.‪a.facialSoundNotify == undefined );
241  assert( self.‪a.currentDialogImportance == undefined );
242  assert( !self.isTalking );
243 
244  self notify( "bc_interrupt" );
245 
246  self.isTalking = true;
247  self.a.facialSoundDone = false;
248  self.a.facialSoundNotify = notifyString;
249  self.a.facialSoundAlias = str_script_alias;
250  self.a.currentDialogImportance = importance;
251 
252  if ( importance == 1.0 )
253  {
254  level.NumberOfImportantPeopleTalking += 1;
255  }
256 
257  /#
258 
259  if ( level.NumberOfImportantPeopleTalking > 1 )
260  {
261  println( "WARNING: multiple high priority dialogs are happening at once " + str_script_alias );
262  }
263 
264  #/
265 
266  uniqueNotify = notifyString + " " + level.TalkNotifySeed;
267 
268  level.TalkNotifySeed += 1;
269 
270  if ( isdefined( level.scr_sound ) && isdefined( level.scr_sound[ "generic" ] ) )
271  {
272  str_vox_file = level.scr_sound[ "generic" ][ str_script_alias ];
273  }
274 
275  if ( isdefined( str_vox_file ) )
276  {
277  if ( SoundExists( str_vox_file ) )
278  {
279  if ( IsPlayer( toplayer ) )
280  {
281  self thread ‪_play_sound_to_player_with_notify( str_vox_file, toplayer, uniqueNotify );
282  }
283  else
284  {
285  if ( isdefined( self GetTagOrigin( "J_Head" ) ) )
286  {
287  self PlaySoundWithNotify( str_vox_file, uniqueNotify, "J_Head" );
288  }
289  else
290  {
291  self PlaySoundWithNotify( str_vox_file, uniqueNotify );
292  }
293  }
294  }
295  else
296  {
297  /#
298  PrintLn( "WARNING: DIALOG VO SOUND: '" + str_script_alias + "' does not exist." );
299  self thread ‪_missing_dialog( str_script_alias, str_vox_file, uniqueNotify );
300  #/
301  }
302  }
303  else
304  {
305  self thread ‪_temp_dialog( str_script_alias, uniqueNotify );
306  }
307 
308  self ‪util::waittill_any( "death", "cancel speaking", uniqueNotify );
309 
310  if ( importance == 1.0 )
311  {
312  level.NumberOfImportantPeopleTalking -= 1;
313  level.ImportantPeopleTalkingTime = GetTime();
314  }
315 
316  if ( isdefined( self ) )
317  {
318  self.isTalking = false;
319  self.a.facialSoundDone = true;
320  self.a.facialSoundNotify = undefined;
321  self.a.facialSoundAlias = undefined;
322  self.a.currentDialogImportance = undefined;
323  self.lastSayTime = GetTime();
324  }
325 
326  self notify( "done speaking", str_notify_alias );
327  self notify( notifyString );
328 }
329 
330 // HACK: there is no way to get a notify from a sound on a specific client.
331 // Even though SoundGetPlaybackTime isn't 100% and won't be accurate with locs,
332 // it's our best bet for now.
333 function ‪_play_sound_to_player_with_notify( soundAlias, toplayer, uniqueNotify )
334 {
335  self endon( "death" );
336  toplayer endon( "death" );
337 
338  self PlaySoundToPlayer( soundAlias, toplayer );
339  n_playbackTime = SoundGetPlaybackTime( soundAlias );
340 
341  if ( n_playbackTime > 0 )
342  {
343  wait n_playbackTime * .001;
344  }
345  else
346  {
347  wait 1.0;
348  }
349 
350  self notify( uniqueNotify );
351 }
352 
353 function private ‪_temp_dialog( str_line, uniqueNotify, b_missing_vo = false )
354 {
355  SetDvar( "bgcache_disablewarninghints", 1 );
356 
357  if ( !b_missing_vo && isdefined( self.propername ) )
358  {
359  str_line = self.propername + ": " + str_line;
360  }
361 
362  foreach ( player in level.players )
363  {
364  if ( !isdefined( player GetLUIMenu( "TempDialog" ) ) )
365  {
366  player OpenLuiMenu( "TempDialog" );
367  }
368 
369  player SetLuiMenuData( player GetLUIMenu( "TempDialog" ), "dialogText", str_line );
370 
371  if ( b_missing_vo )
372  {
373  player SetLuiMenuData( player GetLUIMenu( "TempDialog" ), "title", "MISSING VO SOUND" );
374  }
375  else
376  {
377  player SetLuiMenuData( player GetLUIMenu( "TempDialog" ), "title", "TEMP VO" );
378  }
379  }
380 
381  // wait time based on average words spoken (2 per second) and capped at 5 seconds total, -1 for propername
382  n_wait_time = ( StrTok( str_line, " ").size - 1 ) / 2;
383  n_wait_time = ‪math::clamp( n_wait_time, 2, 5);
384  ‪util::waittill_any_timeout( n_wait_time, "death", "cancel speaking" );
385 
386  foreach ( player in level.players )
387  {
388  if ( isdefined( player GetLUIMenu( "TempDialog" ) ) )
389  {
390  player CloseLUIMenu( player GetLUIMenu( "TempDialog" ) );
391  }
392  }
393 
394  SetDvar( "bgcache_disablewarninghints", 0 );
395 
396  self notify( uniqueNotify );
397 }
398 
399 function private ‪_missing_dialog( str_script_alias, str_vox_file, uniqueNotify )
400 {
401  ‪_temp_dialog( "script id: " + str_script_alias + " sound alias: " + str_vox_file, uniqueNotify, true );
402 }
403 
404 function ‪PlayFace_WaitForNotify(waitForString, notifyString, killmeString)
405 {
406  self endon ("death");
407  self endon (killmeString);
408  self waittill (waitForString);
409  self.a.faceWaitForResult = "notify";
410  self notify (notifyString);
411 }
412 
413 function ‪PlayFace_WaitForTime(time, notifyString, killmeString)
414 {
415  self endon ("death");
416  self endon (killmeString);
417  wait (time);
418  self.a.faceWaitForResult = "time";
419  self notify (notifyString);
420 }
421 
‪PlayFace_WaitForTime
‪function PlayFace_WaitForTime(time, notifyString, killmeString)
Definition: face.gsc:413
‪SaySpecificDialogue
‪function SaySpecificDialogue(facialanim, soundAlias, importance, notifyString, waitOrNot, timeToWait, toplayer)
Definition: face.gsc:106
‪waittill_any_timeout
‪function waittill_any_timeout(n_timeout, string1, string2, string3, string4, string5)
Definition: util_shared.csc:423
‪_play_sound_to_player_with_notify
‪function _play_sound_to_player_with_notify(soundAlias, toplayer, uniqueNotify)
Definition: face.gsc:333
‪a
‪function add_remove_list a
Definition: util_shared.csc:906
‪SayGenericDialogue
‪function SayGenericDialogue(typeString)
Definition: face.gsc:15
‪DEFAULT
‪#define DEFAULT(__var, __default)
Definition: shared.gsh:270
‪waittill_any
‪function waittill_any(str_notify1, str_notify2, str_notify3, str_notify4, str_notify5)
Definition: util_shared.csc:375
‪SetIdleFaceDelayed
‪function SetIdleFaceDelayed(facialAnimationArray)
Definition: face.gsc:76
‪SayGenericDialogueWithImportance
‪function SayGenericDialogueWithImportance(typeString, importance)
Definition: face.gsc:54
‪PlayIdleFace
‪function PlayIdleFace()
Definition: face.gsc:158
‪_missing_dialog
‪function private _missing_dialog(str_script_alias, str_vox_file, uniqueNotify)
Definition: face.gsc:399
‪clamp
‪function clamp(val, val_min, val_max)
Definition: math_shared.csc:16
‪PlayFaceThread
‪function PlayFaceThread(facialanim, str_script_alias, importance, notifyString, waitOrNot, timeToWait, toplayer)
Definition: face.gsc:168
‪PlayFace_WaitForNotify
‪function PlayFace_WaitForNotify(waitForString, notifyString, killmeString)
Definition: face.gsc:404
‪SetIdleFace
‪function SetIdleFace(facialAnimationArray)
Definition: face.gsc:84
‪_temp_dialog
‪function private _temp_dialog(str_line, uniqueNotify, b_missing_vo=false)
Definition: face.gsc:353