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
mission.cpp
Go to the documentation of this file.
1 /*
2  * Vega Strike
3  * Copyright (C) 2001-2002 Daniel Horn
4  *
5  * http://vegastrike.sourceforge.net/
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21 
22 /*
23  * xml Mission written by Alexander Rawass <alexannika@users.sourceforge.net>
24  */
25 #include "config.h"
26 #include <Python.h>
27 #include <math.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <time.h>
32 #include <ctype.h>
33 #ifndef WIN32
34 //this file isn't available on my system (all win32 machines?) i dun even know what it has or if we need it as I can compile without it
35 #include <unistd.h>
36 #endif
37 
38 #include <assert.h>
39 #include "cmd/unit_generic.h"
40 #include "mission.h"
41 #include "flightgroup.h"
42 
43 #ifdef HAVE_PYTHON
44 #include "Python.h"
45 #endif
46 #include "python/python_class.h"
47 #include "savegame.h"
48 #include "networking/netserver.h"
49 
50 /* *********************************************************** */
51 using std::cout;
52 using std::cerr;
53 using std::endl;
55 {
56  VSFileSystem::vs_fprintf( stderr, "Mission Cleanup Not Yet Implemented" );
57  //do not delete msgcenter...could be vital
58 }
59 double Mission::gametime = 0.0;
62 int Mission:: total_nr_frames = 0;
63 vector< Flightgroup* >Mission::flightgroups;
64 
65 Mission::Mission( const char *filename, const std::string &script, bool loadscripts )
66 {
67  ConstructMission( filename, script, loadscripts );
68 }
69 Mission::Mission( const char *filename, bool loadscripts )
70 {
71  ConstructMission( filename, string( "" ), loadscripts );
72 }
73 void Mission::ConstructMission( const char *configfile, const std::string &script, bool loadscripts )
74 {
76  player_num = 0;
77  briefing = NULL;
78  director = NULL;
79  runtime.pymissions = NULL;
80  nextpythonmission = NULL;
81  if (script.length() > 0) {
82  nextpythonmission = new char[script.length()+2];
83  nextpythonmission[script.length()+1] = 0;
84  nextpythonmission[script.length()] = 0;
85  strcpy( nextpythonmission, script.c_str() );
86  }
87  easyDomFactory< missionNode > *domf = new easyDomFactory< missionNode > (); //such a bloody leak!
88 
89  top = domf->LoadXML( configfile );
90  static bool dontpanic = false;
91  if (top == NULL && !dontpanic) {
92  cout<<"Panic exit - mission file "<<configfile<<" not found"<<endl;
93  exit( 0 );
94  } else {
95  dontpanic = true;
96  }
97  if (top == NULL)
98  return;
99 
100  variables = NULL;
101  origin_node = NULL;
102 
103 #ifndef VS_MIS_SEL
104  director = NULL;
105  if (loadscripts) {
106  initTagMap();
107  initCallbackMaps();
108 
109  top->Tag( &tagmap );
110  }
111 #endif
112 }
114 {
115  Unit *Nawl = NULL;
116  if (Owner != Nawl) {
117  Unit *ret = Owner.GetUnit();
118  if (ret == NULL)
119  objective = ""; //unit died
120  return ret;
121  }
122  return Owner.GetUnit();
123 }
125 void Mission::initMission( bool loadscripts )
126 {
127  if (!top)
128  return;
129  if (msgcenter == NULL)
130  msgcenter = new MessageCenter();
131  checkMission( top, loadscripts );
132  mission_name = getVariable( "mission_name", "" );
133 }
134 
135 /* *********************************************************** */
136 
137 bool Mission::checkMission( easyDomNode *node, bool loadscripts )
138 {
139  if (node->Name() != "mission") {
140  cout<<"this is no Vegastrike mission file"<<endl;
141  return false;
142  }
143  vector< easyDomNode* >::const_iterator siter;
144  for (siter = node->subnodes.begin(); siter != node->subnodes.end(); siter++) {
145  if ( (*siter)->Name() == "variables" ) {
146  doVariables( *siter );
147  } else if ( ( (*siter)->Name() == "flightgroups" ) ) {
148  doFlightgroups( *siter );
149  } else if ( ( (*siter)->Name() == "settings" ) ) {
150  doSettings( *siter );
151  } else if ( ( (*siter)->Name() == "module" ) ) {
152  if (loadscripts)
153  DirectorStart( (missionNode*) *siter );
154  } else if ( ( (*siter)->Name() == "python" ) && (!this->nextpythonmission) ) {
155  //I need to get rid of an extra whitespace at the end that expat may have added... Python is VERY strict about that... :(
156  string locals = (*siter)->attr_value( textAttr );
157  const char *constdumbstr = locals.c_str(); //get the text XML attribute
158  int i = strlen( constdumbstr ); //constdumbstr is the string I wish to copy... i is its length.
159  char *dumbstr = new char[i+2]; //allocate 2 extra bytes for a double-null-terminated string.
160  strncpy( dumbstr, constdumbstr, i ); //i copy constdumbstr to dumbstr.
161  dumbstr[i] = '\0'; //I make sure that it has 2 null bytes at the end.
162  dumbstr[i+1] = '\0'; //I am allowed to use i+1 because I allocated 2 extra bytes
163  for (i -= 1; i >= 0; i--) {
164  //start from the end-1, or i-1 and set i to that value(i-=1)
165  if (dumbstr[i] == '\t' || dumbstr[i] == ' ' || dumbstr[i] == '\r' || dumbstr[i] == '\n') {
166  //I check if dumbstr[i] contains whitespace
167  dumbstr[i] = '\0'; //if so, I truncate the string
168  } else {
169  dumbstr[i+1] = '\n'; //otherwise I add a new line (python sometimes gets mad...)
170  dumbstr[i+2] = '\0'; //and add a null byte (If i is at the end of the allocated memory, I will use the extra byte
171  break; //get out of the loop so that it doesn't endlessly delete the newlines that I added.
172  }
173  }
174  this->nextpythonmission = dumbstr;
175  } else {
176  cout<<"warning: Unknown tag: "<<(*siter)->Name()<<endl;
177  }
178  }
179  return true;
180 }
181 
182 static std::vector< Mission* >Mission_delqueue;
184 {
185  while ( !Mission_delqueue.empty() ) {
186  delete Mission_delqueue.back();
187  Mission_delqueue.pop_back();
188  }
189 }
190 
192 {
193  int num = 0;
194 
195  vector< Mission* > *active_missions = ::active_missions.Get();
196  vector< Mission* >::iterator pl = active_missions->begin();
197 
198  if ( pl == active_missions->end() )
199  return -1;
200 
201  for (; pl != active_missions->end(); ++pl) {
202  if ( (*pl)->player_num == this->player_num ) {
203  if (*pl == this)
204  return (int)num;
205  else
206  num++;
207  }
208  }
209 
210  return -1;
211 }
212 Mission* Mission::getNthPlayerMission( int cp, int missionnum )
213 {
214  vector< Mission* > *active_missions = ::active_missions.Get();
215  Mission *activeMis = NULL;
216  if (missionnum >= 0) {
217  int num = -1;
218  vector< Mission* >::iterator pl = active_missions->begin();
219  if ( pl == active_missions->end() ) return NULL;
220  for (; pl != active_missions->end(); ++pl) {
221  if ( (*pl)->player_num == (unsigned int) cp )
222  num++;
223  if (num == missionnum) {
224  //Found it!
225  activeMis = (*pl);
226  break;
227  }
228  }
229  }
230  return activeMis;
231 }
233 {
234  vector< Mission* > *active_missions = ::active_missions.Get();
235  vector< Mission* >::iterator f;
236 
237  f = std::find( Mission_delqueue.begin(), Mission_delqueue.end(), this );
238  if (f != Mission_delqueue.end())
239  VSFileSystem::vs_dprintf( 1, "Not deleting mission twice: %s\n", this->mission_name.c_str() );
240 
241  f = std::find( active_missions->begin(), active_missions->end(), this );
242 
243  // Debugging aid for persistent missions bug
244  if (g_game.vsdebug >= 1) {
245  int misnum = -1;
246  for (vector< Mission* >::iterator i = active_missions->begin(); i != active_missions->end(); ++i) {
247  if ((*i)->player_num == player_num) {
248  ++misnum;
249  VSFileSystem::vs_dprintf( 1, " Mission #%d: %s\n", misnum, (*i)->mission_name.c_str() );
250  }
251  }
252  }
253 
254  int queuenum = -1;
255  if ( f != active_missions->end() ) {
256  queuenum = getPlayerMissionNumber(); //-1 used as error code, 0 is first player mission
257  if ( (Network != NULL || SERVER) && player_num >= 0 ) {
258  int num = queuenum;
259  if (num >= 0)
260  if (SERVER && num > 0)
262  }
263  active_missions->erase( f );
264  }
265  if (this != (*active_missions)[0]) //Shouldn't this always be true?
266  Mission_delqueue.push_back( this ); //only delete if we arent' the base mission
267  //NETFIXME: This routine does not work properly yet.
268  VSFileSystem::vs_dprintf( 1, "Terminating mission %s #%d\n", this->mission_name.c_str(), queuenum );
269  if (queuenum >= 0) {
270  // queuenum - 1 since mission #0 is the base mission (main_menu) and is persisted
271  // in savegame.cpp:LoadSavedMissions, and it has no correspondin active_scripts/active_missions entry,
272  // meaning the actual active_scripts index is offset by 1.
273  unsigned int num = queuenum - 1;
274 
275  vector< std::string > *scripts = &_Universe->AccessCockpit( player_num )->savegame->getMissionStringData(
276  "active_scripts" );
277  VSFileSystem::vs_dprintf( 1, "Terminating mission #%d - got %d scripts\n", queuenum, scripts->size() );
278  if ( num < scripts->size() )
279  scripts->erase( scripts->begin()+num );
280  vector< std::string > *missions = &_Universe->AccessCockpit( player_num )->savegame->getMissionStringData(
281  "active_missions" );
282  VSFileSystem::vs_dprintf( 1, "Terminating mission #%d - got %d missions\n", queuenum, missions->size() );
283  if ( num < missions->size() )
284  missions->erase( missions->begin()+num );
285  VSFileSystem::vs_dprintf( 1, "Terminating mission #%d - %d scripts remain\n", queuenum, scripts->size() );
286  VSFileSystem::vs_dprintf( 1, "Terminating mission #%d - %d missions remain\n", queuenum, missions->size() );
287  }
288  if (runtime.pymissions)
290  runtime.pymissions = NULL;
291 }
292 
293 /* *********************************************************** */
294 
295 void Mission::doOrigin( easyDomNode *node )
296 {
297  origin_node = node;
298 }
299 
300 /* *********************************************************** */
301 
302 #ifndef VS_MIS_SEL
303 void Mission::GetOrigin( QVector &pos, string &planetname )
304 {
305  if (origin_node == NULL) {
306  pos.i = pos.j = pos.k = 0.0;
307  planetname = string();
308  return;
309  }
310  bool ok = doPosition( origin_node, &pos.i, NULL );
311  if (!ok)
312  pos.i = pos.j = pos.k = 0.0;
313  planetname = origin_node->attr_value( "planet" );
314 }
315 
316 #endif
317 /* *********************************************************** */
318 
319 void Mission::doSettings( easyDomNode *node )
320 {
321  vector< easyDomNode* >::const_iterator siter;
322  for (siter = node->subnodes.begin(); siter != node->subnodes.end(); siter++) {
323  easyDomNode *mnode = *siter;
324  if (mnode->Name() == "origin")
325  doOrigin( mnode );
326  }
327 }
328 
329 /* *********************************************************** */
330 
331 void Mission::doVariables( easyDomNode *node )
332 {
333  if (variables != NULL) {
334  cout<<"only one variable section allowed"<<endl;
335  return;
336  }
337  variables = node;
338 
339  vector< easyDomNode* >::const_iterator siter;
340  for (siter = node->subnodes.begin(); siter != node->subnodes.end(); siter++)
341  checkVar( *siter );
342 }
343 
344 /* *********************************************************** */
345 
346 void Mission::checkVar( easyDomNode *node )
347 {
348  if (node->Name() != "var") {
349  cout<<"not a variable"<<endl;
350  return;
351  }
352  string name = node->attr_value( "name" );
353  string value = node->attr_value( "value" );
354 }
355 
356 /* *********************************************************** */
357 
358 void Mission::doFlightgroups( easyDomNode *node )
359 {
360  vector< easyDomNode* >::const_iterator siter;
361  for (siter = node->subnodes.begin(); siter != node->subnodes.end(); siter++)
362  checkFlightgroup( *siter );
363 }
365 {
366  fg->flightgroup_nr = flightgroups.size();
367  flightgroups.push_back( fg );
369 }
370 /* *********************************************************** */
371 
372 void Mission::checkFlightgroup( easyDomNode *node )
373 {
374  if (node->Name() != "flightgroup") {
375  cout<<"not a flightgroup"<<endl;
376  return;
377  }
378  //nothing yet
379  string texture = node->attr_value( "logo" );
380  string texture_alpha = node->attr_value( "logo_alpha" );
381  string name = node->attr_value( "name" );
382  string faction = node->attr_value( "faction" );
383  string type = node->attr_value( "type" );
384  string ainame = node->attr_value( "ainame" );
385  string waves = node->attr_value( "waves" );
386  string nr_ships = node->attr_value( "nr_ships" );
387  string terrain_nr = node->attr_value( "terrain_nr" );
388  string unittype = node->attr_value( "unit_type" );
389  if ( name.empty() || faction.empty() || type.empty() || ainame.empty() || waves.empty() || nr_ships.empty() ) {
390  cout<<"no valid flightgroup decsription"<<endl;
391  return;
392  }
393  if ( unittype.empty() )
394  unittype = string( "unit" );
395  int waves_i = atoi( waves.c_str() );
396  int nr_ships_i = atoi( nr_ships.c_str() );
397 
398  bool have_pos = false;
399  bool have_rot = false;
400 
401  double pos[3];
402  float rot[3];
403 
404  rot[0] = rot[1] = rot[2] = 0.0;
406  cf.fg = Flightgroup::newFlightgroup( name, type, faction, ainame, nr_ships_i, waves_i, texture, texture_alpha, this );
407  vector< easyDomNode* >::const_iterator siter;
408  for (siter = node->subnodes.begin(); siter != node->subnodes.end(); siter++) {
409  if ( (*siter)->Name() == "pos" )
410  have_pos = doPosition( *siter, pos, &cf );
411  else if ( (*siter)->Name() == "rot" )
412  have_rot = doRotation( *siter, rot, &cf );
413  else if ( (*siter)->Name() == "order" )
414  doOrder( *siter, cf.fg );
415  }
416  if (!have_pos)
417  cout<<"don;t have a position in flightgroup "<<name<<endl;
418  if ( terrain_nr.empty() ) {
419  cf.terrain_nr = -1;
420  } else {
421  if (terrain_nr == "mission")
422  cf.terrain_nr = -2;
423  else
424  cf.terrain_nr = atoi( terrain_nr.c_str() );
425  }
427  if (unittype == "vehicle")
429  if (unittype == "building")
431  cf.nr_ships = nr_ships_i;
432  cf.domnode = (node); //don't hijack node
433 
434  cf.fg->pos.i = pos[0];
435  cf.fg->pos.j = pos[1];
436  cf.fg->pos.k = pos[2];
437  for (int i = 0; i < 3; i++)
438  cf.rot[i] = rot[i];
439  cf.nr_ships = nr_ships_i;
440  if (ainame[0] == '_') {
441 #ifndef VS_MIS_SEL
442  addModule( ainame.substr( 1 ) );
443 #endif
444  }
445  number_of_ships += nr_ships_i;
446 }
447 
448 /* *********************************************************** */
449 
450 bool Mission::doPosition( easyDomNode *node, double pos[3], CreateFlightgroup *cf )
451 {
452  string x = node->attr_value( "x" );
453  string y = node->attr_value( "y" );
454  string z = node->attr_value( "z" );
455  if ( x.empty() || y.empty() || z.empty() ) {
456  cout<<"no valid position"<<endl;
457  return false;
458  }
459  pos[0] = strtod( x.c_str(), NULL );
460  pos[1] = strtod( y.c_str(), NULL );
461  pos[2] = strtod( z.c_str(), NULL );
462  if (cf != NULL) {
463  pos[0] += cf->fg->pos.i;
464  pos[1] += cf->fg->pos.j;
465  pos[2] += cf->fg->pos.k;
466  }
467  return true;
468 }
469 
470 /* *********************************************************** */
471 
472 Flightgroup* Mission::findFlightgroup( const string &offset_name, const string &fac )
473 {
474  vector< Flightgroup* >::const_iterator siter;
475  for (siter = flightgroups.begin(); siter != flightgroups.end(); siter++)
476  if ( (*siter)->name == offset_name && (fac.empty() || (*siter)->faction == fac) )
477  return *siter;
478  return NULL;
479 }
480 
481 /* *********************************************************** */
482 
483 bool Mission::doRotation( easyDomNode *node, float rot[3], class CreateFlightgroup* )
484 {
485  //not yet
486  return true;
487 }
488 
489 /* *********************************************************** */
490 
491 void Mission::doOrder( easyDomNode *node, Flightgroup *fg )
492 {
493  //nothing yet
494  string order = node->attr_value( "order" );
495  string target = node->attr_value( "target" );
496  if ( order.empty() || target.empty() ) {
497  cout<<"you have to give an order and a target"<<endl;
498  return;
499  }
500  //the tmptarget is evaluated later
501  //because the target may be a flightgroup that's not yet defined
502  fg->ordermap[order] = target;
503 }
504 
505 /* *********************************************************** */
506 
507 string Mission::getVariable( string name, string defaultval )
508 {
509  vector< easyDomNode* >::const_iterator siter;
510  for (siter = variables->subnodes.begin(); siter != variables->subnodes.end(); siter++) {
511  string scan_name = (*siter)->attr_value( "name" );
512  if (scan_name == name)
513  return (*siter)->attr_value( "value" );
514  }
515  return defaultval;
516 }
517