Vegastrike 0.5.1 rc1  1.0
Original sources for Vegastrike Evolved
•All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
communication.cpp
Go to the documentation of this file.
1 #include "communication.h"
2 #include "vs_globals.h"
3 #include "config_xml.h"
4 #include <assert.h>
5 #include "audiolib.h"
6 #include "options.h"
7 
8 FSM::FSM(const std::string& filename)
9 {
10  //loads a conversation finite state machine with deltaRelation weight transition from an XML?
11  if (filename.empty()) {
12  nodes.push_back( Node::MakeNode( "welcome to cachunkcachunk.com", 0 ) );
13  nodes.push_back( Node::MakeNode( "I love you!", .1 ) );
14  nodes.push_back( Node::MakeNode( "J00 0wnz m3", .08 ) );
15  nodes.push_back( Node::MakeNode( "You are cool!", .06 ) );
16  nodes.push_back( Node::MakeNode( "You are nice!", .05 ) );
17  nodes.push_back( Node::MakeNode( "Ya you're naled! NALED PAL!", -.02 ) );
18  nodes.push_back( Node::MakeNode( "i 0wnz j00", -.08 ) );
19  nodes.push_back( Node::MakeNode( "I hate you!", -.1 ) );
20 
21  nodes.push_back( Node::MakeNode( "Docking operation complete.", 0 ) );
22  nodes.push_back( Node::MakeNode( "Please move into a green docking box and press d.", 0 ) );
23  nodes.push_back( Node::MakeNode( "Docking operation begun.", 0 ) );
24  nodes.push_back( Node::MakeNode( "Clearance denied.", 0 ) );
25  nodes.push_back( Node::MakeNode( "Clearance granted.", 0 ) );
26  nodes.push_back( Node::MakeNode( "No.", 0 ) );
27  nodes.push_back( Node::MakeNode( "Yes.", 0 ) );
28  nodes.push_back( Node::MakeNode( "Prepare To Be Searched. Maintain Speed and Course.", 0 ) );
29  nodes.push_back( Node::MakeNode( "No contraband detected: You may proceed.", 0 ) );
30  nodes.push_back( Node::MakeNode( "Contraband detected! All units close and engage!", 0 ) );
31  nodes.push_back( Node::MakeNode( "Your Course is deviating! Maintain Course!", 0 ) );
32  nodes.push_back( Node::MakeNode( "Request Clearence To Land.", 0 ) );
33  nodes.push_back( Node::MakeNode( "*hit*", -.2 ) );
34  vector< unsigned int >edges;
35  unsigned int i;
36  for (i = 0; i < nodes.size()-13; i++)
37  edges.push_back( i );
38  for (i = 0; i < nodes.size(); i++)
39  nodes[i].edges = edges;
40  } else {
41  LoadXML( filename.c_str() );
42  }
43 }
44 
45 int FSM::GetUnDockNode() const
46 {
47  return nodes.size()-16;
48 }
49 
51 {
52  return nodes.size()-15;
53 }
54 
55 int FSM::GetDockNode() const
56 {
57  return nodes.size()-14;
58 }
59 
61 {
62  return nodes.size()-13;
63 }
64 
66 {
67  return nodes.size()-12;
68 }
69 
70 int FSM::GetNoNode() const
71 {
72  return nodes.size()-11;
73 }
74 
75 int FSM::GetYesNode() const
76 {
77  return nodes.size()-10;
78 }
79 
81 {
82  return nodes.size()-9;
83 }
84 
86 {
87  return nodes.size()-8;
88 }
89 
91 {
92  return nodes.size()-7;
93 }
94 
96 {
97  return nodes.size()-6;
98 }
99 
101 {
102  return nodes.size()-5;
103 }
104 
105 int FSM::GetHitNode() const
106 {
107  return nodes.size()-1;
108 }
109 
111 {
112  return nodes.size()-2;
113 }
114 
116 {
117  return nodes.size()-3;
118 }
119 
121 {
122  return nodes.size()-4;
123 }
124 
125 static float sq( float i )
126 {
127  return i*i;
128 }
129 
130 bool nonneg( float i )
131 {
132  return i >= 0;
133 }
134 
135 std::string FSM::Node::GetMessage( unsigned int &multiple ) const
136 {
137  multiple = rand()%messages.size();
138  return messages[multiple];
139 }
140 
141 // createSound, implemented in unit_functions.cpp / libaudioserver.cpp
142 // FIXME: this variability makes it hard to use proper include files
143 extern int createSound( std::string file, bool val );
144 
145 int FSM::Node::GetSound( unsigned char sex, unsigned int multiple )
146 {
147  unsigned int index = multiple+( (unsigned int) sex )*messages.size();
148  if ( index < sounds.size() ) {
149  if (sounds[index] < 0) {
150  sounds[index] = createSound(soundfiles[index], false);
151  return sounds[index];
152  } else {
153  return sounds[index];
154  }
155  } else {
156  return -1;
157  }
158 }
159 
160 bool FSM::Node::StopSound( unsigned char sex )
161 {
162  unsigned int index = ( (unsigned int) sex )*messages.size();
163  bool ret = false;
164  for (unsigned int i = index; i < index+messages.size() && i < sounds.size(); ++i)
165  if ( sounds[i] > 0 && AUDIsPlaying( sounds[i] ) ) {
166  AUDStopPlaying( sounds[i] );
167  ret = true;
168  }
169  return ret;
170 }
171 
172 bool FSM::StopAllSounds( unsigned char sex )
173 {
174  bool ret = false;
175  for (unsigned int i = 0; i < nodes.size(); ++i)
176  if ( nodes[i].StopSound( sex ) )
177  ret = true;
178  return ret;
179 }
180 
181 void FSM::Node::AddSound( std::string soundfile, unsigned char sex )
182 {
183  static std::string emptystr;
184 
185  for (int multiple = 0;; ++multiple) {
186  unsigned int index = ( (unsigned int) sex )*messages.size()+multiple;
187  while ( index >= sounds.size() ) {
188  sounds.push_back( -1 );
189  soundfiles.push_back(emptystr);
190  }
191 
192  if (soundfiles[index].empty()) {
193  soundfiles[index] = soundfile;
194 
195  // Preload sound if configured to do so
197  GetSound(sex, multiple);
198 
199  break;
200  }
201  }
202 }
203 
204 int FSM::getCommMessageMood( int curstate, float mood, float randomresponse, float relationship ) const
205 {
206  const FSM::Node *n = (unsigned int) curstate
207  < nodes.size() ? (&nodes[curstate]) : (&nodes[getDefaultState( relationship )]);
208  mood += -randomresponse+2*randomresponse*( (float) rand() )/RAND_MAX;
209 
210  int choice = 0;
211 #if 0
212  float bestchoice = 4;
213  bool fitmood = false;
214  for (unsigned i = 0; i < n->edges.size(); i++) {
215  float md = nodes[n->edges[i]].messagedelta;
216  bool newfitmood = nonneg( mood ) == nonneg( md );
217  if ( (!fitmood) || newfitmood ) {
218  float newbestchoice = sq( md-mood );
219  if ( (newbestchoice <= bestchoice) || (fitmood == false && newfitmood == true) )
220  if ( (newbestchoice == bestchoice && rand()%2) || newbestchoice < bestchoice ) {
221  //to make sure some variety happens
222  fitmood = newfitmood;
223  choice = i;
224  bestchoice = newbestchoice;
225  }
226  }
227  }
228 #endif
229  vector< unsigned int >g;
230  vector< unsigned int >b;
231  static float pos_limit = XMLSupport::parse_float( vs_config->getVariable( "AI",
232  "LowestPositiveCommChoice",
233  "0" ) );
234  static float neg_limit = XMLSupport::parse_float( vs_config->getVariable( "AI",
235  "LowestNegativeCommChoice",
236  "-.00001" ) );
237  for (unsigned int i = 0; i < n->edges.size(); i++) {
238  float md = nodes[n->edges[i]].messagedelta;
239  if (md >= pos_limit)
240  g.push_back( i );
241  if (md <= neg_limit)
242  b.push_back( i );
243  }
244  if ( g.size() != 0 && ( relationship > 0 || (b.size() == 0) ) )
245  choice = g[( rand()%g.size() )];
246  else if ( b.size() )
247  choice = b[rand()%b.size()];
248  return choice;
249 }
250 
251 int FSM::getDefaultState( float relationship ) const
252 {
253  if (relationship < -1) relationship = -1;
254  if (relationship > 1) relationship = 1; //clamp it
255  float mood = relationship;
256  float randomresponse = .01;
257  int curstate = 0;
258 
259  const FSM::Node *n = &nodes[curstate];
260  mood += -randomresponse+2*randomresponse*( (float) rand() )/RAND_MAX;
261 
262  int choice = 0;
263  float bestchoice = 16;
264  bool fitmood = false;
265  for (unsigned i = 0; i < n->edges.size(); i++) {
266  float md = nodes[n->edges[i]].messagedelta;
267  bool newfitmood = nonneg( mood ) == nonneg( md );
268  if ( (!fitmood) || newfitmood ) {
269  float newbestchoice = sq( md-mood );
270  if ( (newbestchoice <= bestchoice) || (fitmood == false && newfitmood == true) ) {
271  if ( (newbestchoice == bestchoice && rand()%2) || newbestchoice < bestchoice ) {
272  //to make sure some variety happens
273  fitmood = newfitmood;
274  choice = i;
275  bestchoice = newbestchoice;
276  }
277  }
278  }
279  } //(0,relationship,.01)
280  return nodes[0].edges[choice];
281 }
282 
283 std::string FSM::GetEdgesString( unsigned int curstate )
284 {
285  std::string retval = "\n";
286  if (nodes.size() <= curstate) {
287  fprintf( stderr, "Error with faction relationship due to %d not being in range of faction\n", curstate );
288  return "\n1. Transmit Error\n2. Transmit Error\n3. Transmit Error\n";
289  }
290  for (unsigned int i = 0; i < nodes[curstate].edges.size(); i++)
291  retval += tostring( (int) ( (i+1)%10 ) )+"."+nodes[nodes[curstate].edges[i]].messages[0]+"\n";
292  static bool print_docking =
293  XMLSupport::parse_bool( vs_config->getVariable( "graphics", "hud", "print_request_docking", "true" ) );
294  if (print_docking)
295  retval += "0. Request Docking Clearence";
296  return retval;
297 }
298 
299 float FSM::getDeltaRelation( int prevstate, unsigned int current_state ) const
300 {
301  if (nodes.size() <= current_state) {
302  fprintf( stderr, "Error with faction relationship due to %d not being in range of faction\n", current_state );
303  return 0;
304  }
305  return nodes[current_state].messagedelta;
306 }
307 
308 void CommunicationMessage::Init( Unit *send, Unit *recv )
309 {
310  if (send == NULL) return;
311  fsm = FactionUtil::GetConversation( send->faction, recv->faction );
312  sender.SetUnit( send );
313  this->prevstate = this->curstate = fsm->getDefaultState( send->getRelation( recv ) );
314  this->edgenum = -1;
315 }
316 
317 float myround( float i )
318 {
319  float j = floor( i );
320  if (i-j >= .5)
321  return j+1;
322  return j;
323 }
324 
325 float myroundclamp( float i )
326 {
327  float j = myround( i );
328  if (j < 0)
329  j = 0;
330  return j;
331 }
332 
333 void CommunicationMessage::SetAnimation( std::vector< Animation* > *ani, unsigned char sex )
334 {
335  this->sex = sex; //for audio
336  if (ani) {
337  if (ani->size() > 0) {
338  float mood = fsm->getDeltaRelation( this->prevstate, this->curstate );
339  mood += .1;
340  mood *= ( ani->size() )/.2;
341  unsigned int index = (unsigned int) myroundclamp( floor( mood ) );
342  if ( index >= ani->size() )
343  index = ani->size()-1;
344  this->ani = (*ani)[index];
345  } else {
346  this->ani = NULL;
347  }
348  } else {
349  this->ani = NULL;
350  }
351 }
352 
353 void CommunicationMessage::SetCurrentState( int msg, std::vector< Animation* > *ani, unsigned char sex )
354 {
355  curstate = msg;
356  SetAnimation( ani, sex );
357  assert( this->curstate >= 0 );
358 }
359 
361  Unit *recv,
362  int messagechoice,
363  std::vector< Animation* > *ani,
364  unsigned char sex )
365 {
366  Init( send, recv );
367  prevstate = fsm->getDefaultState( send->getRelation( recv ) );
368  if ( fsm->nodes[prevstate].edges.size() ) {
369  this->edgenum = messagechoice%fsm->nodes[prevstate].edges.size();
370  curstate = fsm->nodes[prevstate].edges[this->edgenum];
371  }
372  SetAnimation( ani, sex );
373  assert( this->curstate >= 0 );
374 }
375 
377  Unit *recv,
378  int laststate,
379  int thisstate,
380  std::vector< Animation* > *ani,
381  unsigned char sex )
382 {
383  Init( send, recv );
384  prevstate = laststate;
385  curstate = thisstate;
386  SetAnimation( ani, sex );
387  assert( this->curstate >= 0 );
388 }
389 
390 CommunicationMessage::CommunicationMessage( Unit *send, Unit *recv, std::vector< Animation* > *ani, unsigned char sex )
391 {
392  Init( send, recv );
393  SetAnimation( ani, sex );
394  assert( this->curstate >= 0 );
395 }
396 
398  Unit *recv,
399  const CommunicationMessage &prevstate,
400  int curstate,
401  std::vector< Animation* > *ani,
402  unsigned char sex )
403 {
404  Init( send, recv );
405  this->prevstate = prevstate.curstate;
406  if ( fsm->nodes[this->prevstate].edges.size() ) {
407  this->edgenum = curstate%fsm->nodes[this->prevstate].edges.size();
408  this->curstate = fsm->nodes[this->prevstate].edges[edgenum];
409  }
410  SetAnimation( ani, sex );
411  assert( this->curstate >= 0 );
412 }
413