1 /***************************************************************************
2                      session.cpp  -  Game session manager
3                              -------------------
4     begin                : Sat May 3 2003
5     copyright            : (C) 2003 by Gabor Torok
6     email                : cctorok@yahoo.com
7 ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 #include "common/constants.h"
18 #include "session.h"
19 #include "render/renderlib.h"
20 #include "rpg/rpglib.h"
21 #include "item.h"
22 #include "creature.h"
23 #include "sqbinding/sqbinding.h"
24 #include "test/combattest.h"
25 #include "shapepalette.h"
26 #include "render/cutscene.h"
27 #include <iostream>
28 #include <stdlib.h>
29 //#include <strings.h>
30 
31 #include "creature.h"
32 #include "persist.h"
33 #include "io/file.h"
34 #include "configlang.h"
35 #include "sound.h"
36 
37 // ###### MS Visual C++ specific ######
38 #if defined(_MSC_VER) && defined(_DEBUG)
39 # define new DEBUG_NEW
40 # undef THIS_FILE
41 static char THIS_FILE[] = __FILE__;
42 #endif
43 
44 using namespace std;
45 
46 Session *Session::instance = NULL;
47 
48 /**
49  *@author Gabor Torok
50  */
Session(GameAdapter * adapter)51 Session::Session( GameAdapter *adapter )
52 		: chapterImage()
53 		, characters( NULL ) {
54 	this->exiting = false;
55 	this->adapter = adapter;
56 	sound = NULL;
57 	shapePal = NULL;
58 	party = NULL;
59 	map = NULL;
60 	board = NULL;
61 	cutscene = NULL;
62 #ifdef HAVE_SDL_NET
63 	server = NULL;
64 	client = NULL;
65 #endif
66 	multiplayerGame = false;
67 	currentMission = NULL;
68 	chapterImageWidth = chapterImageHeight = 0;
69 	showChapterIntro = false;
70 	squirrel = NULL;
71 	savegame = "";
72 	loadgame = "";
73 	strcpy( scoreid, "" );
74 	dataInitialized = NOT_INITIALIZED;
75 	Session::instance = this;
76 	autosave = false;
77 	terrainGenerator = NULL;
78 	strcpy( interruptFunction, "" );
79 }
80 
~Session()81 Session::~Session() {
82 	if( !isExiting() ) {
83 		SpecialSkill::unInitSkills();
84 		Monster::unInitMonsters();
85 		delete characters;
86 		MagicSchool::unInitMagic();
87 		Rpg::unInitRpg();
88 		if ( squirrel ) delete squirrel;
89 		deleteCreaturesAndItems();
90 		delete party;
91 		party = NULL; // adapter destruction may need it
92 		delete sound;
93 		if ( board ) delete board;
94 		if ( cutscene ) delete cutscene;
95 	#ifdef HAVE_SDL_NET
96 		delete server;
97 		delete client;
98 	#endif
99 		if ( map ) delete map;
100 		delete shapePal;
101 		shapePal = NULL; // adapter destruction may need it
102 		delete adapter;
103 	}
104 }
105 
initialize()106 void Session::initialize() {
107 	sound = new Sound( adapter->getPreferences() );
108 	shapePal = new ShapePalette( this );
109 	cutscene = new Cutscene( this );
110 	adapter->setSession( this );
111 	adapter->initVideo();
112 	shapePal->preInitialize();
113 	// init the fonts and ui
114 	//adapter->initStart(12, "Loading shapes...");
115 }
116 
start()117 void Session::start() {
118 	adapter->start();
119 }
120 
doInitData()121 void Session::doInitData() {
122 	adapter->setUpdate( _( "Loading Shapes" ) );
123 	shapePal->initialize();
124 
125 	// read the skills, etc.
126 	adapter->setUpdate( _( "Loading Professions" ) );
127 	Rpg::initRpg();
128 
129 	// initialize the items
130 	adapter->setUpdate( _( "Loading Items" ) );
131 	RpgItem::initItems( shapePal );
132 
133 	// initialize magic
134 	adapter->setUpdate( _( "Loading Spells" ) );
135 	MagicSchool::initMagic();
136 
137 	// init professions
138 	adapter->setUpdate( _( "Loading Characters" ) );
139 	characters = new Characters;
140 
141 	// initialize the monsters (they use items, magic)
142 	adapter->setUpdate( _( "Loading Creatures" ) );
143 	Monster::initMonsters();
144 
145 	adapter->setUpdate( _( "Loading Portraits" ) );
146 	shapePal->loadNpcPortraits();
147 
148 
149 	// create the mission board
150 	adapter->setUpdate( _( "Loading Missions" ) );
151 	board = new Board( this );
152 
153 
154 	// do this before the backpack and optionsdialog (so Z is less than of those)
155 	adapter->setUpdate( _( "Initializing Party" ) );
156 	party = new Party( this );
157 
158 	adapter->setUpdate( _( "Initializing Scripting" ) );
159 	squirrel = new SqBinding( this );
160 
161 	adapter->setUpdate( _( "Loading Skills" ) );
162 	SpecialSkill::initSkills();
163 
164 	adapter->setUpdate( _( "Creating Map" ) );
165 	map = new Map( adapter, adapter->getPreferences(), getShapePalette() );
166 
167 	adapter->setUpdate( _( "Initializing UI" ) );
168 	adapter->initUI();
169 
170 	adapter->setUpdate( "" );
171 }
172 
initData()173 void Session::initData() {
174 	if ( dataInitialized == NOT_INITIALIZED ) {
175 		dataInitialized = INIT_STARTED;
176 		doInitData();
177 		dataInitialized = INIT_DONE;
178 	}
179 }
180 
quit(int value)181 void Session::quit( int value ) {
182 	// FIXME: if(getSDLHandler()) getSDLHandler()->quit(value);
183 	exit( value );
184 }
185 
186 #ifdef HAVE_SDL_NET
runServer(int port)187 void Session::runServer( int port ) {
188 	GameStateHandler *gsh = new TestGameStateHandler();
189 	server = new Server( port ? port : DEFAULT_SERVER_PORT );
190 	server->setGameStateHandler( gsh );
191 
192 	// wait for the server to quit
193 	int status;
194 	SDL_WaitThread( server->getThread(), &status );
195 
196 	delete gsh;
197 }
198 
runClient(char const * host,int port,char const * userName)199 void Session::runClient( char const* host, int port, char const* userName ) {
200 	CommandInterpreter *ci = new TestCommandInterpreter();
201 	GameStateHandler *gsh = new TestGameStateHandler();
202 	client = new Client( host, port, userName, ci );
203 	client->setGameStateHandler( gsh );
204 	if ( !client->login() ) {
205 		cerr << Constants::getMessage( Constants::CLIENT_CANT_CONNECT_ERROR ) << endl;
206 		return;
207 	}
208 
209 	// connect as a character
210 //  Party *party = new Party(this);
211 	Creature *pc[MAX_PARTY_SIZE];
212 	int pcCount;
213 	Party::createHardCodedParty( this, pc, &pcCount );
214 	cerr << "Sending character: " << pc[0]->getName() << endl;
215 	getParty()->resetMultiplayer( pc[0] );
216 
217 	char message[80];
218 	while ( true ) {
219 		cout << "> ";
220 		int c;
221 		int n = 0;
222 		while ( n < 79 && ( c = getchar() ) != '\n' ) message[n++] = c;
223 		message[n] = 0;
224 		client->sendChatTCP( message );
225 		//client->sendRawTCP(message);
226 	}
227 
228 	delete ci;
229 	delete gsh;
230 }
231 
startServer(GameStateHandler * gsh,int port)232 void Session::startServer( GameStateHandler *gsh, int port ) {
233 	server = new Server( port );
234 	server->setGameStateHandler( gsh );
235 	multiplayerGame = true;
236 }
237 
startClient(GameStateHandler * gsh,CommandInterpreter * ci,char const * host,int port,char const * username)238 void Session::startClient( GameStateHandler *gsh, CommandInterpreter *ci, char const* host, int port, char const* username ) {
239 	client = new Client( host, port, username, ci );
240 	client->setGameStateHandler( gsh );
241 	multiplayerGame = true;
242 }
243 
stopClientServer()244 void Session::stopClientServer() {
245 	if ( server ) {
246 		delete server;
247 		server = NULL;
248 	}
249 	if ( client ) {
250 		delete client;
251 		client = NULL;
252 	}
253 	multiplayerGame = false;
254 }
255 
256 #endif
257 
newItem(RpgItem * rpgItem,int level,Spell * spell,bool loading)258 Item *Session::newItem( RpgItem *rpgItem, int level, Spell *spell, bool loading ) {
259 	// don't randomize special items
260 	if ( rpgItem->isSpecial() ) loading = true;
261 	int itemLevel = level;
262 	if ( !loading ) {
263 		itemLevel += Util::dice( 6 ) - 3;
264 	}
265 	if ( itemLevel < 1 ) itemLevel = 1;
266 	Item *item = new Item( this, rpgItem, itemLevel );
267 	if ( spell ) item->setSpell( spell );
268 	newItems.push_back( item );
269 	if ( rpgItem->isSpecial() ) setSpecialItem( rpgItem, item );
270 	return item;
271 }
272 
addItemFromScript(char * name,int x,int y,int z,bool isContainer,int level,int depth)273 Item *Session::addItemFromScript( char *name, int x, int y, int z, bool isContainer, int level, int depth ) {
274 	RpgItem *rpgItem = RpgItem::getItemByName( name );
275 	if ( !rpgItem ) {
276 		cerr << "*** Error: no item named " << name << endl;
277 		return NULL;
278 	}
279 	Item *item = newItem( rpgItem );
280 	// register with squirrel
281 	getSquirrel()->registerItem( item );
282 
283 	if ( isContainer ) {
284 		getGameAdapter()->fillContainer( item, level, depth );
285 	}
286 
287 	getMap()->setItem( x, y, z, item );
288 
289 	return item;
290 }
291 
addCreatureFromScript(char * creatureType,int cx,int cy,int * fx,int * fy)292 Creature *Session::addCreatureFromScript( char *creatureType, int cx, int cy, int *fx, int *fy ) {
293 	Monster *monster = Monster::getMonsterByName( creatureType );
294 	if ( !monster ) {
295 		cerr << "*** Error: no monster named " << creatureType << endl;
296 		return NULL;
297 	}
298 	GLShape *shape = getShapePalette()->
299 	                 getCreatureShape( monster->getModelName(),
300 	                                   monster->getSkinName(),
301 	                                   monster->getScale(),
302 	                                   monster );
303 	Creature *replacement = newCreature( monster, shape );
304 
305 	// register with squirrel
306 	getSquirrel()->registerCreature( replacement );
307 	for ( int i = 0; i < replacement->getBackpackContentsCount(); i++ ) {
308 		getSquirrel()->registerItem( replacement->getBackpackItem( i ) );
309 	}
310 
311 	if ( fx && fy ) {
312 		replacement->findPlace( cx, cy, fx, fy );
313 	} else {
314 		//int ffx, ffy;
315 		//replacement->findPlace( cx, cy, &ffx, &ffy );
316 		replacement->moveTo( cx, cy, 0 );
317 		getMap()->setCreature( cx, cy, 0, replacement );
318 	}
319 	replacement->cancelTarget();
320 
321 	return replacement;
322 }
323 
replaceCreature(Creature * creature,char * newCreatureType)324 Creature *Session::replaceCreature( Creature *creature, char *newCreatureType ) {
325 	int cx = toint( creature->getX() );
326 	int cy = toint( creature->getY() );
327 	int cz = toint( creature->getZ() );
328 	getMap()->removeCreature( cx, cy, cz );
329 	creature->moveTo( -1, -1, 0 ); // remove its marker
330 	creature->setStateMod( StateMod::dead, true ); // make sure it doesn't move
331 
332 	int fx, fy;
333 	Creature *replacement = addCreatureFromScript( newCreatureType, cx, cy, &fx, &fy );
334 	if ( replacement ) {
335 		getMap()->startEffect( fx, fy, 1,
336 		                       Constants::EFFECT_DUST,
337 		                       ( Constants::DAMAGE_DURATION * 4 ),
338 		                       replacement->getShape()->getWidth(),
339 		                       replacement->getShape()->getDepth() );
340 		char msg[120];
341 		snprintf( msg, 120, _( "%s transforms into another shape in front of your very eyes!" ),
342 		          creature->getName() );
343 		getGameAdapter()->writeLogMessage( msg );
344 
345 		cerr << "is npc? " << replacement->isNpc() << endl;
346 	}
347 	return replacement;
348 }
349 
setVisible(Creature * creature,bool b)350 void Session::setVisible( Creature *creature, bool b ) {
351 	if ( b && !isVisible( creature ) ) {
352 		nonVisibleCreatures.erase( creature );
353 		getMap()->setCreature( toint( creature->getX() ), toint( creature->getY() ), toint( creature->getZ() ), creature );
354 	} else if ( !b && isVisible( creature ) ) {
355 		nonVisibleCreatures.insert( creature );
356 		getMap()->removeCreature( toint( creature->getX() ), toint( creature->getY() ), toint( creature->getZ() ) );
357 	}
358 }
359 
isVisible(Creature * creature)360 bool Session::isVisible( Creature *creature ) {
361 	return( nonVisibleCreatures.find( creature ) == nonVisibleCreatures.end() );
362 }
363 
364 // creatures created for the mission
newCreature(Monster * monster,GLShape * shape,bool loaded)365 Creature *Session::newCreature( Monster *monster, GLShape *shape, bool loaded ) {
366 	Creature *c = new Creature( this, monster, shape, !loaded );
367 	creatures.push_back( c );
368 	return c;
369 }
370 
newCreature(Character * character,char const * name,int sex,int model)371 Creature *Session::newCreature( Character *character, char const* name, int sex, int model ) {
372 	Creature *c = new Creature( this, character, name, sex, model );
373 	creatures.push_back( c );
374 	return c;
375 }
376 
removeCreatureRef(Creature * creature,int index)377 bool Session::removeCreatureRef( Creature *creature, int index ) {
378 	for ( vector<Creature*>::iterator i = creatures.begin(); i != creatures.end(); ++i ) {
379 		Creature *c = *i;
380 		if ( c == creature ) {
381 			creatures.erase( i );
382 			return true;
383 		}
384 	}
385 	return false;
386 }
387 
addCreatureRef(Creature * creature,int index)388 void Session::addCreatureRef( Creature *creature, int index ) {
389 	creatures.push_back( creature );
390 }
391 
deleteCreaturesAndItems(bool missionItemsOnly)392 void Session::deleteCreaturesAndItems( bool missionItemsOnly ) {
393 	// delete the items and creatures created for this mission
394 	// (except items in backpack)
395 	if ( !missionItemsOnly ) {
396 		for ( int i = 0; i < static_cast<int>( newItems.size() ); i++ ) {
397 			if ( newItems[i]->isSpecial() ) {
398 				// put special item back into play
399 				special.erase( newItems[i]->getRpgItem() );
400 			}
401 			delete newItems[i];
402 		}
403 		newItems.clear();
404 	} else {
405 		for ( int i = 0; i < static_cast<int>( newItems.size() ); i++ ) {
406 			bool inBackpack = false;
407 			for ( int t = 0; t < getParty()->getPartySize(); t++ ) {
408 				if ( getParty()->getParty( t )->isItemInBackpack( newItems[i] ) ) {
409 					inBackpack = true;
410 					break;
411 				}
412 			}
413 			if ( !inBackpack ) {
414 				if ( newItems[i]->isSpecial() ) {
415 					// put special item back into play
416 					special.erase( newItems[i]->getRpgItem() );
417 				}
418 				delete newItems[i];
419 				for ( int t = i + 1; t < static_cast<int>( newItems.size() ); t++ ) {
420 					newItems[t - 1] = newItems[t];
421 				}
422 				newItems.pop_back();
423 				i--;
424 			}
425 		}
426 	}
427 	for ( int i = 0; i < static_cast<int>( creatures.size() ); i++ ) {
428 		delete creatures[i];
429 	}
430 	creatures.clear();
431 	nonVisibleCreatures.clear();
432 
433 	/*
434 	cerr << "***************************************" << endl;
435 	  cerr << "After mission: " <<
436 	  " creatureCount=" << creatureCount <<
437 	  " itemCount=" << itemCount << endl;
438 	cerr << "***************************************" << endl;
439 	*/
440 	getShapePalette()->debugLoadedModels();
441 }
442 
removeCreature(Creature * creature)443 bool Session::removeCreature( Creature *creature ) {
444 	getMap()->removeCreature( toint( creature->getX() ), toint( creature->getY() ), toint( creature->getZ() ) );
445 	creature->moveTo( -1, -1, 0 ); // remove its marker
446 	creature->setStateMod( StateMod::dead, true ); // make sure it doesn't move
447 	return removeCreatureRef( creature, 0 );
448 }
449 
450 
451 /// Return the closest (visible) monster within the given radius or null if none can be found.
452 
getClosestMonster(int x,int y,int w,int h,int radius)453 Creature *Session::getClosestMonster( int x, int y, int w, int h, int radius ) {
454 	float dist;
455 	float minDist = 0;
456 	Creature *p = NULL;
457 	for ( int i = 0; i < getCreatureCount(); i++ ) {
458 		if ( !getCreature( i )->getStateMod( StateMod::dead ) && !getCreature( i )->getStateMod( StateMod::possessed ) && getCreature( i )->isMonster() && map->isLocationVisible( toint( getCreature( i )->getX() ), toint( getCreature( i )->getY() ) ) && map->isLocationInLight( toint( getCreature( i )->getX() ), toint( getCreature( i )->getY() ), getCreature( i )->getShape() ) ) {
459 			dist = Constants::distance( x, y, w, h, getCreature( i )->getX(), getCreature( i )->getY(), getCreature( i )->getShape()->getWidth(), getCreature( i )->getShape()->getDepth() );
460 			if ( dist <= static_cast<float>( radius ) && ( !p || dist < minDist ) ) {
461 				p = getCreature( i );
462 				minDist = dist;
463 			}
464 		}
465 	}
466 	return p;
467 }
468 
469 /// Return the closest (visible) non-monster creature within the given radius or null if none can be found.
470 
getClosestGoodGuy(int x,int y,int w,int h,int radius)471 Creature *Session::getClosestGoodGuy( int x, int y, int w, int h, int radius ) {
472 	float dist;
473 	float minDist = 0;
474 	Creature *p = NULL;
475 
476 	// Search for the nearest non-monster, non-harmless creature.
477 	for ( int i = 0; i < getCreatureCount(); i++ ) {
478 		if ( !getCreature( i )->getStateMod( StateMod::dead ) && !getCreature( i )->getStateMod( StateMod::possessed ) && map->isLocationInLight( toint( getCreature( i )->getX() ), toint( getCreature( i )->getY() ), getCreature( i )->getShape() ) && !( getCreature( i )->isMonster() || getCreature( i )->isHarmlessAnimal() ) ) {
479 			dist = Constants::distance( x, y, w, h, getCreature( i )->getX(), getCreature( i )->getY(), getCreature( i )->getShape()->getWidth(), getCreature( i )->getShape()->getDepth() );
480 			if ( dist <= static_cast<float>( radius ) && ( !p || dist < minDist ) ) {
481 				p = getCreature( i );
482 				minDist = dist;
483 			}
484 		}
485 	}
486 
487 	// Check whether any party members are nearer.
488 	for ( int i = 0; i < getParty()->getPartySize(); i++ ) {
489 		if ( !getParty()->getParty(i)->getStateMod( StateMod::dead ) && !getParty()->getParty(i)->getStateMod( StateMod::possessed ) ) {
490 			dist = Constants::distance( x, y, w, h, getParty()->getParty(i)->getX(), getParty()->getParty(i)->getY(), getParty()->getParty(i)->getShape()->getWidth(), getParty()->getParty(i)->getShape()->getDepth() );
491 			if ( dist <= static_cast<float>( radius ) && ( !p || dist < minDist ) ) {
492 				p = getParty()->getParty(i);
493 				minDist = dist;
494 			}
495 		}
496 	}
497 
498 	return p;
499 }
500 
501 /// Return a random (visible) monster within the given radius or null if none can be found.
502 
getRandomNearbyMonster(int x,int y,int w,int h,int radius)503 Creature *Session::getRandomNearbyMonster( int x, int y, int w, int h, int radius ) {
504 	vector<Creature*> possibleTargets;
505 	Creature *p = NULL;
506 
507 	for ( int i = 0; i < getCreatureCount(); i++ ) {
508 		if ( !getCreature( i )->getStateMod( StateMod::dead ) && !getCreature( i )->getStateMod( StateMod::possessed ) && map->isLocationVisible( toint( getCreature( i )->getX() ), toint( getCreature( i )->getY() ) ) && map->isLocationInLight( toint( getCreature( i )->getX() ), toint( getCreature( i )->getY() ), getCreature( i )->getShape() ) && getCreature( i )->isMonster() ) {
509 			if ( Constants::distance( x, y, w, h, getCreature( i )->getX(), getCreature( i )->getY(), getCreature( i )->getShape()->getWidth(), getCreature( i )->getShape()->getDepth() ) <= (float)radius ) {
510 				possibleTargets.push_back ( getCreature( i ) );
511 			}
512 		}
513 	}
514 
515 	if ( !possibleTargets.empty() ) p = possibleTargets [ Util::pickOne( 0, possibleTargets.size() - 1 ) ];
516 	return p;
517 }
518 
519 /// Return a random (visible) non-monster within the given radius or null if none can be found.
520 
getRandomNearbyGoodGuy(int x,int y,int w,int h,int radius)521 Creature *Session::getRandomNearbyGoodGuy( int x, int y, int w, int h, int radius ) {
522 	vector<Creature*> possibleTargets;
523 	Creature *p = NULL;
524 
525 	for ( int i = 0; i < getCreatureCount(); i++ ) {
526 		if ( !getCreature( i )->getStateMod( StateMod::dead ) && !getCreature( i )->getStateMod( StateMod::possessed ) && map->isLocationVisible( toint( getCreature( i )->getX() ), toint( getCreature( i )->getY() ) ) && map->isLocationInLight( toint( getCreature( i )->getX() ), toint( getCreature( i )->getY() ), getCreature( i )->getShape() ) && !( getCreature( i )->isMonster() || getCreature( i )->isHarmlessAnimal() ) ) {
527 			if ( Constants::distance( x, y, w, h, getCreature( i )->getX(), getCreature( i )->getY(), getCreature( i )->getShape()->getWidth(), getCreature( i )->getShape()->getDepth() ) <= (float)radius ) {
528 				possibleTargets.push_back ( getCreature( i ) );
529 			}
530 		}
531 	}
532 
533 	for ( int i = 0; i < getParty()->getPartySize(); i++ ) {
534 		if ( !getParty()->getParty(i)->getStateMod( StateMod::dead ) && !getParty()->getParty(i)->getStateMod( StateMod::possessed ) ) {
535 			if ( Constants::distance( x, y, w, h, getParty()->getParty(i)->getX(), getParty()->getParty(i)->getY(), getParty()->getParty(i)->getShape()->getWidth(), getParty()->getParty(i)->getShape()->getDepth() ) <= (float)radius ) {
536 				possibleTargets.push_back ( getParty()->getParty( i ) );
537 			}
538 		}
539 	}
540 
541 	if ( !possibleTargets.empty() ) p = possibleTargets [ Util::pickOne( 0, possibleTargets.size() - 1 ) ];
542 	return p;
543 }
544 
creatureDeath(Creature * creature)545 void Session::creatureDeath( Creature *creature ) {
546 	bool result;
547 	squirrel->callBoolMethod( "creatureDeath",
548 	                          squirrel->getCreatureRef( creature ),
549 	                          &result );
550 	// FIXME: not used currently
551 	//if( !result ) return;
552 
553 	// dismiss summoned creatures
554 	creature->dismissSummonedCreatures();
555 
556 	if ( creature == party->getPlayer() ) {
557 		party->switchToNextLivePartyMember();
558 	}
559 	// remove from the map; the object will be cleaned up at the end of the mission
560 	map->removeCreature( toint( creature->getX() ),
561 	                     toint( creature->getY() ),
562 	                     toint( creature->getZ() ) );
563 	// add a container object instead
564 	//if(battleRound.size() > 0) creature->getShape()->setCurrentAnimation(MD2_DEATH1);
565 	Item *item = newItem( RpgItem::getItemByName( "Corpse" ) );
566 	// add creature's backpack to container
567 	map->setItem( toint( creature->getX() ),
568 	              toint( creature->getY() ),
569 	              toint( creature->getZ() ), item );
570 	int n = creature->getBackpackContentsCount();
571 	for ( int i = 0; i < n; i++ ) {
572 		// make it contain all items, no matter what size
573 		item->addContainedItem( creature->removeFromBackpack( 0 ), true );
574 	}
575 	creature->setStateMod( StateMod::dead, true );
576 
577 	creature->setCauseOfDeath( creature->getPendingCauseOfDeath() );
578 
579 	// cancel target, otherwise segfaults on resurrection
580 	creature->cancelTarget();
581 
582 	if ( creature->isPartyMember() ) {
583 		char message[255];
584 		snprintf( message, 255, _( "  %s dies!" ), creature->getName() );
585 		getGameAdapter()->startTextEffect( message );
586 	}
587 
588 #ifdef HAVE_SDL_NET
589 	bool foundLivePlayer = false;
590 	for ( int i = 0; i < getParty()->getPartySize(); i++ ) {
591 		if ( !getParty()->getParty( i )->getStateMod( StateMod::dead ) ) {
592 			foundLivePlayer = true;
593 			break;
594 		}
595 	}
596 	if ( !foundLivePlayer ) {
597 		/*
598 		Hack: only info about the lead player is uploaded. Set the cause of death on that
599 		player now. This is a hack so cod need not be persisted in savegame.
600 		*/
601 		getParty()->getParty( 0 )->setCauseOfDeath( creature->getCauseOfDeath() );
602 		getGameAdapter()->askToUploadScore();
603 	}
604 #endif
605 }
606 
607 // define below to enable savegame testing
608 //#define TESTING_SAVEGAME 1
609 
testSaveGame(Session * session)610 void testSaveGame( Session *session ) {
611 	cerr << "Loading savegame." << endl;
612 	Uint32 storylineIndex;
613 	Uint32 partySize;
614 	Creature *pc[MAX_PARTY_SIZE];
615 
616 	{
617 		FILE *fp = fopen( "/home/gabor/.scourge/savegame.dat", "rb" );
618 		File *file = new File( fp );
619 		Uint32 n = PERSIST_VERSION;
620 		file->read( &n );
621 		file->read( &storylineIndex );
622 		file->read( &partySize );
623 		cerr << "LOADING: " << endl;
624 		for ( int i = 0; i < static_cast<int>( partySize ); i++ ) {
625 			CreatureInfo *info = Persist::loadCreature( file );
626 			pc[i] = Creature::load( session, info );
627 			if ( i == 0 ) {
628 				for ( int t = 0; t < Skill::SKILL_COUNT; t++ ) {
629 					cerr << "\tinfo=" << info->skills[t] <<
630 					" infoMOD=" << info->skillMod[t] <<
631 					" SK=" << pc[i]->getSkill( t, false ) <<
632 					" MOD=" << pc[i]->getSkillMod( t ) <<
633 					" BON=" << pc[i]->getSkillBonus( t ) <<
634 					endl;
635 				}
636 			}
637 			Persist::deleteCreatureInfo( info );
638 		}
639 		delete file;
640 	}
641 
642 	cerr << "Saving savegame." << endl;
643 	{
644 		FILE *fp = fopen( "/home/gabor/.scourge/out.dat", "wb" );
645 		if ( !fp ) {
646 			cerr << "Error creating savegame file!" << endl;
647 			return;
648 		}
649 		File *file = new File( fp );
650 		Uint32 n = PERSIST_VERSION;
651 		file->write( &n );
652 		file->write( &storylineIndex );
653 		file->write( &partySize );
654 		for ( int i = 0; i < static_cast<int>( partySize ); i++ ) {
655 			CreatureInfo *info = pc[i]->save();
656 			Persist::saveCreature( file, info );
657 			Persist::deleteCreatureInfo( info );
658 		}
659 		delete file;
660 	}
661 
662 	cerr << "AFTER SAVING: " << endl;
663 	for ( int t = 0; t < Skill::SKILL_COUNT; t++ ) {
664 		cerr << "\tSK=" << pc[0]->getSkill( t, false ) << endl;
665 	}
666 
667 	cerr << "Done." << endl;
668 }
669 
670 //#define TESTING_CONFIG
671 
runGame(GameAdapter * adapter,int argc,char * argv[])672 int Session::runGame( GameAdapter *adapter, int argc, char *argv[] ) {
673 
674 	int err = Constants::initRootDir( argc, argv );
675 	if ( err ) return err;
676 
677 #ifdef TESTING_CONFIG
678 	ConfigLang *config = ConfigLang::load( "config/scourge.cfg" );
679 	config->debug();
680 	delete config;
681 	exit( 0 );
682 #endif
683 
684 #ifdef TESTING_SAVEGAME
685 	adapter = new GameAdapter( adapter->getPreferences() );
686 #endif
687 	// -=K=-: its sole session ... so i make it static
688 	static Session session( adapter );
689 	session.initialize();
690 	if ( argc >= 2 && !strcmp( argv[1], "--run-tests" ) ) {
691 		char const* path = ( argc >= 3 ?
692 		                     argv[2] :
693 		                     "/home/gabor/sourceforge/scourge/api/tests" );
694 		if ( CombatTest::executeTests( &session, path ) ) {
695 			cout << "Tests were succesfully written to: " << path << endl;
696 			return 0;
697 		} else {
698 			cerr << "Error while running tests." << endl;
699 			return 1;
700 		}
701 		return 0;
702 	}
703 #ifndef TESTING_SAVEGAME
704 	char* str = new char[100]; strcpy(str,"leak #7 (5 unknown leaks)"); // a test leak
705 	session.start();
706 #else
707 	testSaveGame( &session );
708 #endif
709 
710 	return EXIT_SUCCESS;
711 }
712 
getCountForDate(char * key,bool withinLastHour)713 int Session::getCountForDate( char *key, bool withinLastHour ) {
714 	int count = 0;
715 	char const* value = getSquirrel()->getValue( key );
716 	if ( value != NULL ) {
717 		char s[255];
718 		strcpy( s, value );
719 		char *p = strtok( s, "+" );
720 		if ( p != NULL ) {
721 			char *q = strtok( NULL, "+" );
722 			Date *lastUsed = new Date( p );
723 			Date now = getParty()->getCalendar()->getCurrentDate();
724 
725 			bool withinDate = ( ( withinLastHour && now.isAnHourLater( *lastUsed ) ) || ( !withinLastHour && now.isADayLater( *lastUsed ) ) );
726 
727 			// did specified amount of time pass?
728 			if ( !withinDate ) {
729 				if ( q ) {
730 					count = atoi( q );
731 				}
732 			}
733 			delete lastUsed;
734 		}
735 	}
736 	return count;
737 }
738 
setCountForDate(char * key,int value)739 void Session::setCountForDate( char *key, int value ) {
740 	char s[255];
741 	snprintf( s, 255, "%s+%d",
742 	          getParty()->getCalendar()->getCurrentDate().getShortString(),
743 	          value );
744 	getSquirrel()->setValue( key, s );
745 }
746 
setSavegameName(string & s)747 void Session::setSavegameName( string& s ) {
748 	savegame = s;
749 }
750 
setSavegameTitle(string & s)751 void Session::setSavegameTitle( string& s ) {
752 	savetitle = s;
753 }
754 
getCreatureByName(char const * name)755 Creature *Session::getCreatureByName( char const* name ) {
756 	for ( unsigned int i = 0; i < creatures.size(); i++ ) {
757 		if ( !strcmp( creatures[i]->getName(), name ) ) return creatures[i];
758 	}
759 	return NULL;
760 }
761 
setCurrentMission(Mission * mission)762 void Session::setCurrentMission( Mission *mission ) {
763 	Mission *oldMission = currentMission;
764 	currentMission = mission;
765 	getGameAdapter()->refreshBackpackUI();
766 	if ( oldMission != currentMission && currentMission && currentMission->isStoryLine() && !currentMission->isReplay() ) {
767 		char filename[300];
768 		snprintf( filename, 300, "chapter%d.png", currentMission->getChapter() );
769 		setChapterImage( filename );
770 	}
771 
772 	// initialize script objects
773 	getSquirrel()->initLevelObjects();
774 }
775 
setChapterImage(char * image)776 void Session::setChapterImage( char *image ) {
777 	char filename[300];
778 	snprintf( filename, 300, "/chapters/%s", image );
779 	chapterImageTexture.load( filename );
780 	if ( !chapterImageTexture.isSpecified() ) {
781 		cerr << "Error loading chapter image " << image << endl;
782 		chapterImage.clear();
783 		// its clear anyway, chapterImageTexture.clear();
784 		chapterImageWidth = chapterImageHeight = 0;
785 	} else {
786 		//chapterImageTexture = shapePal->loadGLTextures(filename, true);
787 		GLclampf pri = 0.1f; chapterImageTexture.glPrioritize( pri );
788 		chapterImageWidth = 1000;
789 		chapterImageHeight = 458;
790 		cerr << "***********************************" << endl;
791 		cerr << "Loaded chapter art: " << filename <<
792 		" dimensions=" << chapterImageWidth << "," << chapterImageHeight << endl;
793 		cerr << "***********************************" << endl;
794 	}
795 }
796 
797 std::string HQ_AMBIENT_SOUND = "hq";
getAmbientSoundName()798 std::string& Session::getAmbientSoundName() {
799 	return getCurrentMission() ? getCurrentMission()->getAmbientSoundName() : HQ_AMBIENT_SOUND;
800 }
801 
playSound(const std::string & sound,int panning)802 void Session::playSound( const std::string& sound, int panning ) {
803 	getSound()->playSound( sound, panning );
804 }
805