1 /***************************************************************************
2               creature.cpp  -  A class describing any creature
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 
18 #include "common/constants.h"
19 #include "creature.h"
20 #include "item.h"
21 #include "pathmanager.h"
22 #include "render/renderlib.h"
23 #include "session.h"
24 #include "shapepalette.h"
25 #include "events/event.h"
26 #include "events/potionexpirationevent.h"
27 #include "events/statemodexpirationevent.h"
28 #include "events/thirsthungerevent.h"
29 #include "sqbinding/sqbinding.h"
30 #include "debug.h"
31 #include "sound.h"
32 
33 using namespace std;
34 
35 //#define DEBUG_CREATURE_AI 1
36 
37 //#define DEBUG_INVENTORY 1
38 
39 #define PERCEPTION_DELTA 2000
40 
41 bool loading = false;
42 
43 //#define DEBUG_CAPABILITIES
44 
45 #define MOVE_DELAY 7
46 
47 // at this fps, the players step 1 square
48 #define FPS_ONE 10.0f
49 
50 // how fast to turn
51 #define TURN_STEP_COUNT 5
52 
53 // how far to move away when in the player's way
54 #define AWAY_DISTANCE 8
55 
56 // how close to stay to the player
57 #define CLOSE_DISTANCE 8
58 
59 /// Describes monster toughness in general.
60 struct MonsterToughness {
61 	float minSkillBase, maxSkillBase;
62 	float minHpMpBase, maxHpMpBase;
63 	float armorMisfuction;
64 };
65 
66 // goes from not very tough to tough
67 MonsterToughness monsterToughness[] = {
68 	{  .5f,  0.8f,     .7f,      1,  .33f },
69 	{ .75f,  0.9f,    .75f,      1,  .15f },
70 	{  .8f,     1,    .75f,  1.25f,   .5f }
71 };
72 
73 
Creature(Session * session,Character * character,char const * name,int sex,int character_model_info_index)74 Creature::Creature( Session *session, Character *character, char const* name, int sex, int character_model_info_index ) : RenderedCreature( session->getPreferences(), session->getShapePalette(), session->getMap() ) {
75 	this->session = session;
76 	this->character = character;
77 	this->monster = NULL;
78 	setName( name );
79 	this->character_model_info_index = character_model_info_index;
80 	this->model_name = session->getShapePalette()->getCharacterModelInfo( sex, character_model_info_index )->model_name;
81 	this->skin_name = session->getShapePalette()->getCharacterModelInfo( sex, character_model_info_index )->skin_name;
82 	this->originalSpeed = this->speed = 5; // start neutral speed
83 	this->motion = Constants::MOTION_MOVE_TOWARDS;
84 	this->armor = 0;
85 	this->armorChanged = true;
86 	this->bonusArmor = 0;
87 	this->thirst = 10;
88 	this->hunger = 10;
89 	this->shape = session->getShapePalette()->getCreatureShape( model_name.c_str(), skin_name.c_str(), session->getShapePalette()->getCharacterModelInfo( sex, character_model_info_index )->scale );
90 	this->sex = sex;
91 	commonInit();
92 }
93 
Creature(Session * session,Monster * monster,GLShape * shape,bool initMonster)94 Creature::Creature( Session *session, Monster *monster, GLShape *shape, bool initMonster ) : RenderedCreature( session->getPreferences(), session->getShapePalette(), session->getMap() ) {
95 	this->session = session;
96 	this->character = NULL;
97 	this->monster = monster;
98 	setName( monster->getDisplayName() );
99 	this->model_name = monster->getModelName();
100 	this->skin_name = monster->getSkinName();
101 	this->originalSpeed = this->speed = monster->getSpeed();
102 	this->motion = Constants::MOTION_LOITER;
103 	this->armor = monster->getBaseArmor();
104 	this->armorChanged = true;
105 	this->bonusArmor = 0;
106 	this->shape = shape;
107 	this->sex = Constants::SEX_MALE;
108 	commonInit();
109 	this->level = monster->getLevel();
110 	if ( initMonster ) monsterInit();
111 }
112 
commonInit()113 void Creature::commonInit() {
114 	this->summoner = NULL;
115 	this->backpack = new Item( session, RpgItem::getItemByName( "Backpack" ), 1 );
116 	this->backpack->setInventoryOf( this );
117 	this->lastDecision = 0;
118 	this->portrait.clear();
119 
120 	this->scripted = false;
121 	this->scriptedAnim = MD2_STAND;
122 	this->lastPerceptionCheck = 0;
123 	this->boss = false;
124 	this->savedMissionObjective = false;
125 
126 	this->backpackSorted = false;
127 
128 	this->causeOfDeath[0] = 0;
129 	( ( AnimatedShape* )shape )->setCreatureSpeed( speed );
130 
131 	for ( int i = 0; i < RpgItem::DAMAGE_TYPE_COUNT; i++ ) {
132 		lastArmor[i] = lastArmorSkill[i] = lastDodgePenalty[i] = 0;
133 	}
134 	for ( int i = 0; i < 12; i++ ) quickSpell[ i ] = NULL;
135 	this->lastMove = 0;
136 	this->moveCount = 0;
137 	this->x = this->y = this->z = 0;
138 	this->dir = Constants::MOVE_UP;
139 	this->formation = DIAMOND_FORMATION;
140 	this->tx = this->ty = -1;
141 	this->selX = this->selY = -1;
142 	this->cantMoveCounter = 0;
143 	this->pathManager = new PathManager( this );
144 
145 	this->preferredWeapon = -1;
146   for(int i = 0; i < Constants::EQUIP_LOCATION_COUNT; i++) {
147 		equipped[i] = MAX_BACKPACK_SIZE;
148 	}
149 	for ( int i = 0; i < Skill::SKILL_COUNT; i++ ) {
150 		skillBonus[i] = skillsUsed[i] = skillMod[i] = 0;
151 	}
152 	//this->stateMod = ( 1 << StateMod::dead ) - 1;
153 	//this->protStateMod = ( 1 << StateMod::dead ) - 1;
154 	this->stateMod = 0;
155 	this->protStateMod = 0;
156 	this->level = 1;
157 	this->experience = 0;
158 	this->hp = 0;
159 	this->mp = 0;
160 	this->startingHp = 0;
161 	this->startingMp = 0;
162 	this->ac = 0;
163 	this->targetCreature = NULL;
164 	this->targetX = this->targetY = this->targetZ = 0;
165 	this->targetItem = NULL;
166 	this->lastTick = 0;
167 	this->lastTurn = 0;
168 	this->facingDirection = Constants::MOVE_UP; // good init ?
169 	this->failedToMoveWithinRangeAttemptCount = 0;
170 	this->action = Constants::ACTION_NO_ACTION;
171 	this->actionItem = NULL;
172 	this->actionSpell = NULL;
173 	this->actionSkill = NULL;
174 	this->preActionTargetCreature = NULL;
175 	this->angle = this->wantedAngle = this->angleStep = 0;
176 	this->portraitTextureIndex = 0;
177 	this->deityIndex = -1;
178 	this->availableSkillMod = 0;
179 	this->hasAvailableSkillPoints = false;
180 
181 	// Yes, monsters have backpack weight issues too
182 	backpackWeight =  0.0f;
183 	for ( int i = 0; i < backpack->getContainedItemCount(); i++ ) {
184 		backpackWeight += backpack->getContainedItem( i )->getWeight();
185 	}
186 	this->money = this->level * Util::dice( 10 );
187 	calculateExpOfNextLevel();
188 	this->battle = new Battle( session, this );
189 
190 	lastEnchantDate.setDate( -1, -1, -1, -1, -1, -1 );
191 
192 	this->npcInfo = NULL;
193 	this->mapChanged = false;
194 	this->moving = false;
195 
196 	evalSpecialSkills();
197 
198 }
199 
~Creature()200 Creature::~Creature() {
201 	portrait.clear();
202 
203 	// cancel this creature's events
204 	Party* party = session->getParty();
205 	if ( party != NULL ) party->getCalendar()->cancelEventsForCreature( this );
206 
207 	// now delete the creature
208 	session->getGameAdapter()->removeBattle( battle );
209 	delete battle;
210 	delete pathManager;
211 	// delete the md2/3 shape
212 	ShapePalette* shapepal = session->getShapePalette();
213 	if ( shapepal != NULL ) {
214 		shapepal->decrementSkinRefCountAndDeleteShape( model_name.c_str(),
215 		                                               skin_name.c_str(),
216 		                                               shape,
217 		                                               monster );
218 	}
219 	// delete the backpack infos
220 	for ( map<Item*, BackpackInfo*>::iterator e = invInfos.begin(); e != invInfos.end(); ++e ) {
221 		BackpackInfo *info = e->second;
222 		delete info;
223 	}
224 	delete backpack;
225 }
226 
227 /// Changes a character-type creature's profession, and applies the effects.
228 
changeProfession(Character * c)229 void Creature::changeProfession( Character *c ) {
230 	enum { MSG_SIZE = 120 };
231 	char message[ MSG_SIZE ];
232 
233 	// boost skills
234 	for ( int i = 0; i < Skill::SKILL_COUNT; i++ ) {
235 		int maxValue = c->getSkill( i );
236 		if ( maxValue > 0 ) {
237 			int oldValue = character->getSkill( i );
238 			int newValue = getSkill( i ) + ( oldValue > 0 ? maxValue - oldValue : maxValue );
239 			setSkill( i, newValue );
240 
241 			snprintf( message, MSG_SIZE, _( "%1$s's skill in %2$s has increased." ), getName(), Skill::skills[ i ]->getDisplayName() );
242 			session->getGameAdapter()->writeLogMessage( message, Constants::MSGTYPE_STATS );
243 		}
244 	}
245 
246 	// remove forbidden items
247 	for( int i = 0; i < Constants::EQUIP_LOCATION_COUNT; i++ ) {
248 		if ( equipped[i] < MAX_BACKPACK_SIZE ) {
249 			Item *item = backpack->getContainedItem( equipped[i] );
250 			if ( !c->canEquip( item->getRpgItem() ) ) {
251 				doff( equipped[i] );
252 				snprintf( message, MSG_SIZE, _( "%1$s is not allowed to equip %2$s." ), getName(), item->getName() );
253 				session->getGameAdapter()->writeLogMessage( message );
254 			}
255 		}
256 	}
257 
258 	// add capabilities?
259 
260 
261 	this->character = c;
262 	setHp();
263 	setMp();
264 }
265 
save()266 CreatureInfo *Creature::save() {
267 	CreatureInfo *info =  new CreatureInfo;
268 	info->version = PERSIST_VERSION;
269 	strncpy( ( char* )info->name, getName(), 254 );
270 	info->name[254] = 0;
271 	if ( isMonster() || isNpc() ) {
272 		strcpy( ( char* )info->character_name, "" );
273 		strcpy( ( char* )info->monster_name, monster->getType() );
274 		info->character_model_info_index = 0;
275 		info->npcInfo = ( isNpc() && getNpcInfo() ? getNpcInfo()->save() : NULL );
276 	} else {
277 		strcpy( ( char* )info->character_name, character->getName() );
278 		strcpy( ( char* )info->monster_name, "" );
279 		info->character_model_info_index = character_model_info_index;
280 		info->npcInfo = NULL;
281 	}
282 	info->deityIndex = deityIndex;
283 	info->hp = hp;
284 	info->mp = mp;
285 	info->exp = experience;
286 	info->level = level;
287 	info->money = money;
288 	info->stateMod = stateMod;
289 	info->protStateMod = protStateMod;
290 	info->x = toint( x );
291 	info->y = toint( y );
292 	info->z = toint( z );
293 	info->dir = dir;
294 	info->speed = originalSpeed;
295 	info->motion = motion;
296 	info->sex = sex;
297 	info->armor = 0;
298 	info->bonusArmor = bonusArmor;
299 	//info->bonusArmor = 0;
300 	info->thirst = thirst;
301 	info->hunger = hunger;
302 	info->availableSkillPoints = availableSkillMod;
303 	for ( int i = 0; i < Skill::SKILL_COUNT; i++ ) {
304 		info->skills[i] = skills[i];
305 		info->skillBonus[i] = skillBonus[i];
306 		info->skillsUsed[i] = skillsUsed[i];
307 		info->skillMod[i] = skillMod[i];
308 	}
309 	info->portraitTextureIndex = portraitTextureIndex;
310 
311 	// backpack
312 	info->backpack_count = backpack->getContainedItemCount();
313 	for ( int i = 0; i < backpack->getContainedItemCount(); i++ ) {
314 		info->backpack[i] = backpack->getContainedItem( i )->save();
315 	}
316   for(int i = 0; i < Constants::EQUIP_LOCATION_COUNT; i++) {
317 		info->equipped[i] = equipped[i];
318 	}
319 
320 	// spells
321 	int count = 0;
322 	for ( int i = 0; i < MagicSchool::getMagicSchoolCount(); i++ ) {
323 		MagicSchool *school = MagicSchool::getMagicSchool( i );
324 		for ( int t = 0; t < school->getSpellCount(); t++ ) {
325 			Spell *spell = school->getSpell( t );
326 			if ( isSpellMemorized( spell ) ) {
327 				strcpy( ( char* )info->spell_name[count++], spell->getName() );
328 			}
329 		}
330 	}
331 	info->spell_count = count;
332 
333 	for ( int i = 0; i < 12; i++ ) {
334 		strcpy( ( char* )info->quick_spell[ i ],
335 		        ( getQuickSpell( i ) ? getQuickSpell( i )->getName() : "" ) );
336 	}
337 
338 	info->boss = ( Uint8 )boss;
339 	info->mission = ( Uint8 )( session->getCurrentMission() && session->getCurrentMission()->isMissionCreature( this ) ? 1 : 0 );
340 
341 	return info;
342 }
343 
load(Session * session,CreatureInfo * info)344 Creature *Creature::load( Session *session, CreatureInfo *info ) {
345 	Creature *creature = NULL;
346 
347 	if ( !strlen( ( char* )info->character_name ) ) {
348 
349 		Monster *monster = Monster::getMonsterByName( ( char* )info->monster_name );
350 		if ( !monster ) {
351 			cerr << "Error: can't find monster: " << ( char* )info->monster_name << endl;
352 			return NULL;
353 		}
354 		GLShape *shape = session->getShapePalette()->
355 		                 getCreatureShape( monster->getModelName(),
356 		                                   monster->getSkinName(),
357 		                                   monster->getScale(),
358 		                                   monster );
359 		creature = session->newCreature( monster, shape, true );
360 		creature->setName( ( char* )info->name ); // important for npc-s
361 		if ( info->npcInfo ) {
362 			NpcInfo *npcInfo = NpcInfo::load( info->npcInfo );
363 			if ( npcInfo ) creature->setNpcInfo( npcInfo );
364 		}
365 
366 		// fixme: throw away this code when saving stats_mods and calendar events is implemented
367 		// for now, set a monsters stat mods as declared in creatures.txt
368 		creature->stateMod = monster->getStartingStateMod();
369 	} else {
370 		creature = new Creature( session,
371 		                         Characters::getByName( ( char* )info->character_name ),
372 		                         ( char* )info->name,
373 		                         info->sex,
374 		                         info->character_model_info_index );
375 	}
376 
377 	// don't recalculate skills
378 	// NOTE: don't call return until loading=false.
379 	loading = true;
380 
381 //  cerr << "*** LOAD: creature=" << info->name << endl;
382 	creature->setDeityIndex( info->deityIndex );
383 	creature->setHp( info->hp );
384 	creature->setMp( info->mp );
385 	creature->setExp( info->exp );
386 	creature->setLevel( info->level );
387 	creature->setMoney( info->money );
388 	creature->moveTo( info->x, info->y, info->z );
389 	creature->setDir( info->dir );
390 	//creature->setSpeed( info->speed );
391 	//creature->setMotion( info->motion );
392 	//creature->setArmor( info->armor );
393 
394 	// info->bonusArmor: can't be used until calendar is also persisted
395 	//creature->setBonusArmor( info->bonusArmor );
396 
397 	creature->setThirst( info->thirst );
398 	creature->setHunger( info->hunger );
399 	creature->setAvailableSkillMod( info->availableSkillPoints );
400 
401 	for ( int i = 0; i < Skill::SKILL_COUNT; i++ ) {
402 		creature->skills[i] = info->skills[i];
403 		// Don't set skillBonus: it's reconstructed via the backpack.
404 		//creature->skillBonus[i] = info->skillBonus[i];
405 		// Don't set skillUsed: it's not used.
406 		//creature->skillsUsed[i] = info->skillsUsed[i];
407 		creature->skillMod[i] = info->skillMod[i];
408 	}
409 
410 	// stateMod and protStateMod not useful until calendar is also persisted
411 	//creature->stateMod = info->stateMod;
412 	//creature->protStateMod = info->protStateMod;
413 	// these two don't req. events:
414 	if ( info->stateMod & ( 1 << StateMod::dead ) ) creature->setStateMod( StateMod::dead, true );
415 	//if(info->stateMod & (1 << Constants::leveled)) creature->setStateMod(Constants::leveled, true);
416 
417 	// backpack
418 	//creature->backpack_count = info->backpack_count;
419 	for ( int i = 0; i < static_cast<int>( info->backpack_count ); i++ ) {
420 		Item *item = Item::load( session, info->backpack[i] );
421 		if ( item ) {
422 			if( !creature->addToBackpack( item ) ) {
423 				cerr << "Warning: could not add item to backpack: " << item->getName() << endl;
424 			}
425 		}
426 	}
427   for(int i = 0; i < Constants::EQUIP_LOCATION_COUNT; i++) {
428 		if ( info->equipped[i] < MAX_BACKPACK_SIZE ) {
429 			creature->equipFromBackpack( info->equipped[i], i );
430 		} else {
431 			creature->equipped[i] = info->equipped[i];
432 		}
433 	}
434 
435 	creature->portraitTextureIndex = info->portraitTextureIndex;
436 	if ( creature->portraitTextureIndex >= session->getShapePalette()->getPortraitCount( creature->getSex() ) )
437 		creature->portraitTextureIndex = session->getShapePalette()->getPortraitCount( creature->getSex() ) - 1;
438 
439 	// spells
440 	for ( int i = 0; i < static_cast<int>( info->spell_count ); i++ ) {
441 		Spell *spell = Spell::getSpellByName( ( char* )info->spell_name[i] );
442 		creature->addSpell( spell );
443 	}
444 
445 	for ( int i = 0; i < 12; i++ ) {
446 		if ( strlen( ( char* )info->quick_spell[ i ] ) ) {
447 			Spell *spell = Spell::getSpellByName( ( char* )info->quick_spell[ i ] );
448 			if ( spell ) creature->setQuickSpell( i, spell );
449 			else {
450 				SpecialSkill *special = SpecialSkill::findByName( ( char* )info->quick_spell[ i ], false );
451 				if ( special ) creature->setQuickSpell( i, special );
452 				else {
453 					// it's an item. Find it
454 					for ( int t = 0; t < creature->getBackpackContentsCount(); t++ ) {
455 						Item *item = creature->getBackpackItem( t );
456 						if ( !strcmp( item->getName(), ( char* )info->quick_spell[ i ] ) ) {
457 							creature->setQuickSpell( i, ( Storable* )item );
458 							break;
459 						}
460 					}
461 				}
462 			}
463 		}
464 	}
465 
466 	creature->setBoss( info->boss != 0 );
467 	creature->setSavedMissionObjective( info->mission != 0 );
468 	if ( creature->isSavedMissionObjective() ) {
469 		cerr << "*********************************" << endl;
470 		cerr << "Loaded mission creature:" << creature->getName() << endl;
471 		cerr << "*********************************" << endl;
472 	}
473 
474 	creature->calculateExpOfNextLevel();
475 
476 	creature->evalSpecialSkills();
477 
478 	// recalculate skills from now on
479 	loading = false;
480 
481 	return creature;
482 }
483 
484 /// Returns the UNLOCALIZED name of a character/monster/NPC.
485 
getType()486 char const* Creature::getType() {
487 	return( monster ? monster->getType() : character->getName() );
488 }
489 
490 /// Gets the number of experience points required to level up.
491 
calculateExpOfNextLevel()492 void Creature::calculateExpOfNextLevel() {
493 	if ( !( isPartyMember() || isWanderingHero() ) ) return;
494 	expOfNextLevel = 0;
495 	for ( int i = 0; i < level; i++ ) {
496 		expOfNextLevel += ( ( i + 1 ) * character->getLevelProgression() );
497 	}
498 }
499 
500 /// Selects (with a 1/10 chance) a random new movement direction.
501 
switchDirection(bool force)502 void Creature::switchDirection( bool force ) {
503 	int n = Util::dice( 10 );
504 	if ( n == 0 || force ) {
505 		int dir = Util::dice( 4 );
506 		switch ( dir ) {
507 		case 0: setDir( Constants::MOVE_UP ); break;
508 		case 1: setDir( Constants::MOVE_DOWN ); break;
509 		case 2: setDir( Constants::MOVE_LEFT ); break;
510 		case 3: setDir( Constants::MOVE_RIGHT ); break;
511 		}
512 	}
513 }
514 
515 /// Moves the creature 1 step into the specified direction.
516 
move(Uint16 dir)517 bool Creature::move( Uint16 dir ) {
518 	//if(character) return false;
519 
520 	// is monster (or npc) doing something else?
521 	if ( ( ( AnimatedShape* )getShape() )->getCurrentAnimation() != MD2_RUN ) return false;
522 
523 	switchDirection( false );
524 
525 	// a hack for runaway creatures
526 	if ( !( x > 10 && x < MAP_WIDTH - 10 &&
527 	        y > 10 && y < MAP_DEPTH - 10 ) ) {
528 		if ( monster ) cerr << "hack for " << getName() << endl;
529 		switchDirection( true );
530 		return false;
531 	}
532 
533 	GLfloat nx = x;
534 	GLfloat ny = y;
535 	GLfloat nz = z;
536 	GLfloat step = getStep();
537 	switch ( dir ) {
538 	case Constants::MOVE_UP:
539 		ny = y - step;
540 		break;
541 	case Constants::MOVE_DOWN:
542 		ny = y + step;
543 		break;
544 	case Constants::MOVE_LEFT:
545 		nx = x - step;
546 		break;
547 	case Constants::MOVE_RIGHT:
548 		nx = x + step;
549 		break;
550 	}
551 	setFacingDirection( dir );
552 
553 	if ( !session->getMap()->moveCreature( toint( x ), toint( y ), toint( z ),
554 	        toint( nx ), toint( ny ), toint( nz ), this ) ) {
555 		( ( AnimatedShape* )shape )->setDir( dir );
556 		if ( session->getMap()->getHasWater() &&
557 		        !( toint( x ) == toint( nx ) &&
558 		           toint( y ) == toint( ny ) ) ) {
559 			session->getMap()->startEffect( toint( getX() + getShape()->getWidth() / 2 ),
560 			    toint( getY() - getShape()->getDepth() / 2 ), 0,
561 			    Constants::EFFECT_RIPPLE, ( Constants::DAMAGE_DURATION * 4 ),
562 			    getShape()->getWidth(), getShape()->getDepth() );
563 		}
564 		moveTo( nx, ny, nz );
565 		setDir( dir );
566 		return true;
567 	} else {
568 		switchDirection( true );
569 		return false;
570 	}
571 }
572 
573 /// Searches for a creature target within the specified range.
574 
setTargetCreature(Creature * c,bool findPath,float range)575 void Creature::setTargetCreature( Creature *c, bool findPath, float range ) {
576 	targetCreature = c;
577 	if ( findPath ) {
578 		if ( !setSelCreature( c , range, false ) ) {
579 			// FIXME: should mark target somehow. Path alg. cannot reach it; blocked by something.
580 			// Keep the target creature anyway.
581 			if ( session->getPreferences()->isBattleTurnBased() ) {
582 
583 				session->getGameAdapter()->writeLogMessage( _( "Can't find path to target. Sorry!" ), Constants::MSGTYPE_SYSTEM );
584 				session->getGameAdapter()->setCursorMode( Constants::CURSOR_FORBIDDEN );
585 			}
586 		}
587 	}
588 }
589 
590 /// Makes the creature follow another creature. Returns true for success.
591 
follow(Creature * leader)592 bool Creature::follow( Creature *leader ) {
593 	float dist = getDistance( leader );
594 	if ( dist < CLOSE_DISTANCE ) {
595 		if ( cantMoveCounter > 5 ) {
596 			cantMoveCounter = 0;
597 		} else {
598 			return true;
599 		}
600 	}
601 	//speed = FAST_SPEED;
602 	// Creature *player = session->getParty()->getPlayer();
603 	//bool found = pathManager->findPathToCreature( leader, player, session->getMap());
604 	return setSelCreature( leader, 1 );
605 }
606 
607 /// Sets where to move the creature. Returns true if the move is possible, false otherwise.
608 
setSelXY(int x,int y,bool cancelIfNotPossible)609 bool Creature::setSelXY( int x, int y, bool cancelIfNotPossible ) {
610 	bool ignoreParty = session->getParty()->getPlayer() == this && !session->getGameAdapter()->inTurnBasedCombat();
611 	int oldSelX = selX;
612 	int oldSelY = selY;
613 	int oldtx = tx;
614 	int oldty = ty;
615 
616 	selX = x;
617 	selY = y;
618 	if ( x < 0 || y < 0 ) return true; //this is often used to deselect
619 
620 	setMotion( Constants::MOTION_MOVE_TOWARDS );
621 
622 	// find the path
623 	tx = selX;
624 	ty = selY;
625 	// Does the path lead to the destination?
626 	bool ret = pathManager->findPath( selX, selY,
627 	           session->getParty()->getPlayer(),
628 	           session->getMap(),
629 	           ignoreParty );
630 
631 	/**
632 	 * For pc-s cancel the move.
633 	 */
634 	if ( !ret && character && cancelIfNotPossible ) {
635 		pathManager->clearPath();
636 		selX = oldSelX;
637 		selY = oldSelY;
638 		tx = oldtx;
639 		ty = oldty;
640 	} else {
641 		//make the selected location equal the end of our path
642 		Location last = pathManager->getEndOfPath();
643 		selX = last.x;
644 		selY = last.y;
645 	}
646 
647 	// FIXME: when to play sound?
648 	if ( ret && session->getParty()->getPlayer() == this ) {
649 		// play command sound
650 		if ( 0 == Util::dice(  session->getPreferences()->getSoundFreq() ) &&
651 		        !getStateMod( StateMod::dead ) ) {
652 			//session->playSound(getCharacter()->getRandomSound(Constants::SOUND_TYPE_COMMAND));
653 			int panning = session->getMap()->getPanningFromMapXY( this->x, this->y );
654 			playCharacterSound( GameAdapter::COMMAND_SOUND, panning );
655 		}
656 	}
657 
658 	return ret;
659 }
660 
661 /// Use this instead of setSelXY when targetting creatures so that it will check all locations occupied by large creatures.
662 
setSelCreature(Creature * creature,float range,bool cancelIfNotPossible)663 bool Creature::setSelCreature( Creature* creature, float range, bool cancelIfNotPossible ) {
664 	bool ignoreParty = session->getParty()->getPlayer() == this && !session->getGameAdapter()->inTurnBasedCombat();
665 	int oldSelX = selX;
666 	int oldSelY = selY;
667 	int oldtx = tx;
668 	int oldty = ty;
669 
670 	selX = toint( creature->getX() + creature->getShape()->getWidth() / 2.0f );
671 	selY = toint( creature->getY() + creature->getShape()->getDepth() / 2.0f );
672 	Creature * oldTarget = targetCreature;
673 
674 	targetCreature = creature;
675 
676 	setMotion( Constants::MOTION_MOVE_TOWARDS );
677 	tx = ty = -1;
678 
679 	// find the path
680 	tx = selX;
681 	ty = selY;
682 	// Does the path lead close enough to the destination?
683 //	bool ret = pathManager->findPathToCreature( creature, session->getParty()->getPlayer(), session->getMap(), range, ignoreParty );
684 	bool ret = pathManager->findPathToCreature( creature, this, session->getMap(), range, ignoreParty );
685 
686 	/**
687 	 * For pc-s cancel the move.
688 	 */
689 	if ( !ret && session->getParty()->isPartyMember(this) && cancelIfNotPossible ) {
690 		pathManager->clearPath();
691 		selX = oldSelX;
692 		selY = oldSelY;
693 		tx = oldtx;
694 		ty = oldty;
695 		targetCreature = oldTarget;
696 	}
697 
698 	// FIXME: when to play sound?
699 	if ( ret && session->getParty()->getPlayer() == this ) {
700 		// play command sound
701 		if ( creature->getX() > -1 &&
702 		        0 == Util::dice(  session->getPreferences()->getSoundFreq() ) &&
703 		        !getStateMod( StateMod::dead ) ) {
704 			//session->playSound(getCharacter()->getRandomSound(Constants::SOUND_TYPE_COMMAND));
705 			int panning = session->getMap()->getPanningFromMapXY( this->x, this->y );
706 			playCharacterSound( GameAdapter::COMMAND_SOUND, panning );
707 		}
708 	}
709 	return ret;
710 }
711 
moveToLocator()712 Location *Creature::moveToLocator() {
713 	Location *pos = NULL;
714 
715 	//we either have a target we want to reach, or we are wandering around
716 	if ( selX > -1 || getMotion() == Constants::MOTION_LOITER ) {
717 		// take a step
718 		pos = takeAStepOnPath();
719 
720 		// did we step on a trap?
721 		evalTrap();
722 
723 		// if we've no more steps
724 		if ( pathManager->atEndOfPath() ) {
725 			stopMoving();
726 			setMotion( Constants::MOTION_STAND );
727 		} else if ( pos ) {
728 			cantMoveCounter++;
729 			if ( isPartyMember() ) {
730 				pathManager->moveNPCsOffPath( this, session->getMap() );
731 			}
732 			if ( cantMoveCounter > 5 ) {
733 				stopMoving();
734 				setMotion( Constants::MOTION_STAND );
735 				cantMoveCounter = 0;
736 			}
737 		} else if ( !pos ) {
738 			cantMoveCounter = 0;
739 			setMoving( true );
740 		}
741 
742 	} else {
743 
744 		if ( !( getMotion() == Constants::MOTION_LOITER || getMotion() == Constants::MOTION_STAND ) ) {
745 #if PATH_DEBUG
746 			cerr << "Creature stuck: " << getName() << endl;
747 #endif
748 			stopMoving();
749 			setMotion( Constants::MOTION_STAND );
750 		}
751 
752 	}
753 
754 	return pos;
755 }
756 
757 /// Move the creature one step along its path. Handles blocking objects.
758 
759 /// Returns the blocking shape or NULL
760 /// if move is possible.
761 
takeAStepOnPath()762 Location *Creature::takeAStepOnPath() {
763 	Location *position = NULL;
764 	int a = ( ( AnimatedShape* )getShape() )->getCurrentAnimation();
765 
766 	if ( !pathManager->atEndOfPath() && a == MD2_RUN ) { //a != MD2_TAUNT ) {
767 
768 		// take a step on the bestPath
769 		Location location = pathManager->getNextStepOnPath();
770 
771 		GLfloat newX = getX();
772 		GLfloat newY = getY();
773 
774 		int cx = toint( newX );
775 		int cy = toint( newY ); //current x,y
776 
777 		GLfloat step = getStep();
778 		float targetX = static_cast<float>( location.x );
779 		float targetY = static_cast<float>( location.y );
780 
781 		//get the direction to the target location
782 		float diffX = targetX - newX; //distance between creature's (continuous) location and the target (discrete) location
783 		float diffY = targetY - newY;
784 		//get the x and y values for a step-length vector in the direction of diffX,diffY
785 		//float dist = sqrt(diffX*diffX + diffY*diffY); //distance to location
786 		float dist = Constants::distance( newX, newY, 0, 0, targetX, targetY, 0, 0 ); //distance to location
787 		//if(dist < step) step = dist; //if the step is too great, we slow ourselves to avoid overstepping
788 		if ( dist != 0.0f ) { // thee shall not divide with zero
789 			float stepX = ( diffX * step ) / dist;
790 			float stepY = ( diffY * step ) / dist;
791 
792 			newY += stepY;
793 			newX += stepX;
794 
795 			int nx = toint( newX );
796 			int ny = toint( newY );
797 
798 			position = session->getMap()->
799 			           moveCreature( cx, cy, toint( getZ() ),
800 			                         nx, ny, toint( getZ() ),
801 			                         this );
802 
803 			if ( position && cx != location.x && cy != location.y && ( ( cx != nx && cy == ny ) || ( cx == nx && cy != ny ) ) ) {
804 #if PATH_DEBUG
805 				cerr << "Popping: " << this->getName() << endl;
806 #endif
807 				//we are blocked at our next step, are moving diagonally, and did not complete the diagonal move
808 				newX = targetX;
809 				newY = targetY; //we just "pop" to the target location
810 				nx = toint( newX );
811 				ny = toint( newY );
812 				position = session->getMap()->
813 				           moveCreature( cx, cy, toint( getZ() ),
814 				                         nx, ny, toint( getZ() ),
815 				                         this );
816 			}
817 		}
818 
819 		if ( !position ) {
820 			computeAngle( newX, newY );
821 			showWaterEffect( newX, newY );
822 			moveTo( newX, newY, getZ() );
823 			if ( toint( newX ) == location.x && toint( newY ) == location.y ) {
824 				pathManager->incrementPositionOnPath();
825 			}
826 		} else {
827 #if PATH_DEBUG
828 			cerr << "Blocked, stopping: " << this->getName() << endl;
829 #endif
830 		}
831 
832 	}
833 
834 	return position;
835 }
836 
837 /// Computes the angle the creature should assume to reach newX, nexY.
838 
computeAngle(GLfloat newX,GLfloat newY)839 void Creature::computeAngle( GLfloat newX, GLfloat newY ) {
840 	GLfloat a = Util::getAngle( newX, newY, 1, 1,
841 	                            getX(), getY(), 1, 1 );
842 
843 	if ( pathManager->atStartOfPath() || a != wantedAngle ) {
844 		wantedAngle = a;
845 		GLfloat diff = Util::diffAngle( a, angle );
846 		angleStep = diff / static_cast<float>( TURN_STEP_COUNT );
847 	}
848 
849 	if ( fabs( angle - wantedAngle ) > 2.0f ) {
850 		GLfloat diff = Util::diffAngle( wantedAngle, angle );
851 		if ( fabs( diff ) < angleStep ) {
852 			angle = wantedAngle;
853 		} else {
854 			angle += angleStep;
855 		}
856 		if ( angle < 0.0f ) angle = 360.0f + angle;
857 		if ( angle >= 360.0f ) angle -= 360.0f;
858 	} else {
859 		angle = wantedAngle;
860 	}
861 
862 	( ( AnimatedShape* )shape )->setAngle( angle + 180.0f );
863 }
864 
showWaterEffect(GLfloat newX,GLfloat newY)865 void Creature::showWaterEffect( GLfloat newX, GLfloat newY ) {
866 	if ( session->getMap()->getHasWater() &&
867 	        !( toint( getX() ) == toint( newX ) &&
868 	           toint( getY() ) == toint( newY ) ) ) {
869 		session->getMap()->startEffect( toint( getX() + getShape()->getWidth() / 2 ),
870 		    toint( getY() - getShape()->getDepth() / 2 ), 0,
871 		    Constants::EFFECT_RIPPLE,
872 		    ( Constants::DAMAGE_DURATION * 4 ),
873 		    getShape()->getWidth(), getShape()->getDepth() );
874 	}
875 }
876 
877 /// Stops movement.
878 
stopMoving()879 void Creature::stopMoving() {
880 	cantMoveCounter = 0;
881 	pathManager->clearPath();
882 	selX = selY = -1;
883 	speed = originalSpeed;
884 	getShape()->setCurrentAnimation( MD2_STAND );
885 	if ( session->getParty()->getPlayer() == this ) session->getSound()->stopFootsteps();
886 }
887 
888 /// Plays the footstep sound.
889 
890 Uint32 lastFootstepTime = 0;
playFootstep()891 void Creature::playFootstep() {
892 	Uint32 now = SDL_GetTicks();
893 	if ( now - lastFootstepTime > ( Uint32 ) ( session->getPreferences()->getGameSpeedTicks() * 4 ) && getMotion() != Constants::MOTION_STAND ) {
894 		int panning = session->getMap()->getPanningFromMapXY( this->x, this->y );
895 		session->getSound()->startFootsteps( session->getAmbientSoundName(), session->getGameAdapter()->getCurrentDepth(), panning );
896 		lastFootstepTime = now;
897 	}
898 }
899 
900 /// Checks whether the creature has any moves left on its path.
901 
anyMovesLeft()902 bool Creature::anyMovesLeft() {
903 	return( selX > -1 && !pathManager->atEndOfPath() );
904 }
905 
906 
907 /// Returns the weight the creature can carry before being overloaded.
908 
getMaxBackpackWeight()909 float Creature::getMaxBackpackWeight() {
910 	return static_cast<float>( getSkill( Skill::POWER ) ) * 2.5f;
911 }
912 
pickUpOnMap(RenderedItem * item)913 void Creature::pickUpOnMap( RenderedItem *item ) {
914 	addToBackpack( ( Item* )item );
915 }
916 
917 /// Adds an item to the creature's backpack.
918 
addToBackpack(Item * item,int itemX,int itemY)919 bool Creature::addToBackpack( Item *item, int itemX, int itemY ) {
920 	BackpackInfo *info = getBackpackInfo( item, true );
921 	if ( backpack->addContainedItem( item, itemX, itemY ) ) {
922 
923 		info->equipIndex = -1;
924 		info->backpackIndex = backpack->getContainedItemCount() - 1;
925 
926 		backpackWeight += item->getWeight();
927 
928 		if ( backpackWeight > getMaxBackpackWeight() ) {
929 			if ( isPartyMember() ) {
930 				char msg[80];
931 				snprintf( msg, 80, _( "%s is overloaded." ), getName() );
932 				session->getGameAdapter()->writeLogMessage( msg, Constants::MSGTYPE_STATS );
933 			}
934 			setStateMod( StateMod::overloaded, true );
935 		}
936 
937 		// check if the mission is over
938 		if ( isPartyMember() &&
939 		        session->getCurrentMission() &&
940 		        session->getCurrentMission()->itemFound( item ) ) {
941 			session->getGameAdapter()->missionCompleted();
942 		}
943 
944 #ifdef DEBUG_INVENTORY
945 	if( this == session->getParty()->getPlayer() ) {
946 		cerr << "Creature::addToBackpack: " << item->getName() << endl;
947 		debugBackpack();
948 	}
949 #endif
950 
951 		return true;
952 	} else {
953 		cerr << "*** error: unable to add to inventory of creature=" << getName() << " item=" << item->getName() << endl;
954 		return false;
955 	}
956 }
957 
958 /// Returns the backpack index and equip index of an item.
959 
getBackpackInfo(Item * item,bool createIfMissing)960 BackpackInfo *Creature::getBackpackInfo( Item *item, bool createIfMissing ) {
961 	if ( invInfos.find( item ) == invInfos.end() ) {
962 		if ( createIfMissing ) {
963 			BackpackInfo *info = new BackpackInfo();
964 			invInfos[ item ] = info;
965 			return info;
966 		} else {
967 			return NULL;
968 		}
969 	} else {
970 		return invInfos[ item ];
971 	}
972 }
973 
974 /// Returns the backpack index of an item.
975 
findInBackpack(Item * item)976 int Creature::findInBackpack( Item *item ) {
977 #ifdef DEBUG_INVENTORY
978 	if( this == session->getParty()->getPlayer() ) {
979 		cerr << "findInBackpack: item=" << item->getName() << " address=" << item << endl;
980 		debugBackpack();
981 	}
982 #endif
983 	BackpackInfo *info = getBackpackInfo( item );
984 	return( info ? info->backpackIndex : -1 );
985 	/*
986 	 for(int i = 0; i < backpack_count; i++) {
987 	   Item *invItem = backpack[i];
988 	   if(item == invItem) return i;
989 	 }
990 	 return -1;
991 	*/
992 }
993 
debugBackpack()994 void Creature::debugBackpack() {
995 	cerr << "**************************************" << endl;
996 	cerr << "backpack size=" << backpack->getContainedItemCount() << endl;
997 	for( int i = 0; i < backpack->getContainedItemCount(); i++ ) {
998 		cerr << "\titem: "  << backpack->getContainedItem( i )->getName() << " address=" << backpack->getContainedItem( i ) << endl;
999 	}
1000 	cerr << "InvInfos size=" << invInfos.size() << endl;
1001 	for( map<Item*, BackpackInfo*>::iterator e = invInfos.begin(); e != invInfos.end(); ++e ) {
1002 		Item *item = e->first;
1003 		BackpackInfo *bpi = e->second;
1004 		cerr << "\titem: " << item->getName() << " address=" << item << " backpack info: " << bpi->backpackIndex << "," << bpi->equipIndex << endl;
1005 	}
1006 	cerr << "Equipped: " << endl;
1007 	for(int i = 0; i < Constants::EQUIP_LOCATION_COUNT; i++) {
1008 		if(equipped[i] < MAX_BACKPACK_SIZE) {
1009 			cerr << "\tat: " << i << " location: " << (1 << i) << " index: " << equipped[i] << " item: " << getBackpackItem(equipped[i])->getName() << " address: " << getBackpackItem(equipped[i]) << endl;
1010 		}
1011 	}
1012 	cerr << "**************************************" << endl;
1013 }
1014 
1015 /// Removes an item from the backpack at index.
1016 
removeFromBackpack(int backpackIndex)1017 Item *Creature::removeFromBackpack( int backpackIndex ) {
1018 #ifdef DEBUG_INVENTORY
1019 	if( this == session->getParty()->getPlayer() ) {
1020 		cerr << "Creature::removeFromBackpack: " << backpackIndex << endl;
1021 		if( this == session->getParty()->getPlayer() ) debugBackpack();
1022 	}
1023 #endif
1024 
1025 	Item *item = NULL;
1026 	if ( backpackIndex < backpack->getContainedItemCount() ) {
1027 		// drop item if carrying it
1028 		doff( backpackIndex );
1029 
1030 		// drop from backpack
1031 		item = backpack->getContainedItem( backpackIndex );
1032 
1033 		BackpackInfo *info = getBackpackInfo( item );
1034 		invInfos.erase( item );
1035 		delete info;
1036 
1037 		// remove it from quickspell slot
1038 		for ( int i = 0; i < 12; i++ ) {
1039 			Storable *storable = getQuickSpell( i );
1040 			if ( storable ) {
1041 				if ( storable->getStorableType() == Storable::ITEM_STORABLE ) {
1042 					if ( ( Item* )storable == item ) {
1043 						setQuickSpell( i, NULL );
1044 					}
1045 				}
1046 			}
1047 		}
1048 
1049 		backpackWeight -= item->getWeight();
1050 		if ( getStateMod( StateMod::overloaded ) && backpackWeight < getMaxBackpackWeight() ) {
1051 			if ( isPartyMember() ) {
1052 				char msg[80];
1053 				snprintf( msg, 80, _( "%s is not overloaded anymore." ), getName() );
1054 				session->getGameAdapter()->writeLogMessage( msg, Constants::MSGTYPE_STATS );
1055 			}
1056 			setStateMod( StateMod::overloaded, false );
1057 		}
1058 
1059 		// remove it from the backpack
1060 		backpack->removeContainedItem( item );
1061 
1062 		// update the backpack infos of items higher than the removed item... (HACK!)
1063 		for ( int i = backpackIndex; i < backpack->getContainedItemCount(); i++ ) {
1064 			BackpackInfo *info = getBackpackInfo( backpack->getContainedItem( i ) );
1065 			info->backpackIndex--;
1066 		}
1067 		// adjust equipped indexes too
1068     for(int i = 0; i < Constants::EQUIP_LOCATION_COUNT; i++) {
1069 			if ( equipped[i] > backpackIndex && equipped[i] < MAX_BACKPACK_SIZE ) {
1070 				equipped[i]--;
1071 			}
1072 		}
1073 		recalcAggregateValues();
1074 	}
1075 	return item;
1076 }
1077 
1078 /// returns true if ate/drank item completely and false else
1079 
eatDrink(int backpackIndex)1080 bool Creature::eatDrink( int backpackIndex ) {
1081 	return eatDrink( getBackpackItem( backpackIndex ) );
1082 }
1083 
eatDrink(Item * item)1084 bool Creature::eatDrink( Item *item ) {
1085 	enum {MSG_SIZE = 500 };
1086 	char msg[MSG_SIZE];
1087 	char buff[200];
1088 	RpgItem * rpgItem = item->getRpgItem();
1089 
1090 	int type = rpgItem->getType();
1091 	//weight = item->getWeight();
1092 	int level = item->getLevel();
1093 	if ( type == RpgItem::FOOD ) {
1094 		if ( getHunger() == 10 ) {
1095 			snprintf( msg, MSG_SIZE, _( "%s is not hungry at the moment." ), getName() );
1096 			session->getGameAdapter()->writeLogMessage( msg );
1097 			return false;
1098 		}
1099 
1100 		// TODO : the quality member of rpgItem should indicate if the
1101 		// food is totally healthy or roten or partially roten etc...
1102 		// We eat the item and it gives us "level" hunger points back
1103 		setHunger( getHunger() + level );
1104 		strcpy( buff, rpgItem->getShortDesc() );
1105 		buff[0] = tolower( buff[0] );
1106 		snprintf( msg, MSG_SIZE, _( "%1$s eats %2$s." ), getName(), buff );
1107 		session->getGameAdapter()->writeLogMessage( msg, Constants::MSGTYPE_PLAYERITEM );
1108 		bool b = item->decrementCharges();
1109 		if ( b ) {
1110 			snprintf( msg, MSG_SIZE, _( "%s is used up." ), item->getItemName() );
1111 			session->getGameAdapter()->writeLogMessage( msg );
1112 		}
1113 		return b;
1114 	} else if ( type == RpgItem::DRINK ) {
1115 		if ( getThirst() == 10 ) {
1116 			snprintf( msg, MSG_SIZE, _( "%s is not thirsty at the moment." ), getName() );
1117 			session->getGameAdapter()->writeLogMessage( msg );
1118 			return false;
1119 		}
1120 		setThirst( getThirst() + level );
1121 		strcpy( buff, rpgItem->getShortDesc() );
1122 		buff[0] = tolower( buff[0] );
1123 		snprintf( msg, MSG_SIZE, _( "%1$s drinks %2$s." ), getName(), buff );
1124 		session->getGameAdapter()->writeLogMessage( msg, Constants::MSGTYPE_PLAYERITEM );
1125 		// TODO : according to the alcool rate set drunk state or not
1126 		bool b = item->decrementCharges();
1127 		if ( b ) {
1128 			snprintf( msg, MSG_SIZE, _( "%s is used up." ), item->getItemName() );
1129 			session->getGameAdapter()->writeLogMessage( msg );
1130 		}
1131 		return b;
1132 	} else if ( type == RpgItem::POTION ) {
1133 		// It's a potion
1134 		// Even if not thirsty, character will always drink a potion
1135 		strcpy( buff, rpgItem->getShortDesc() );
1136 		buff[0] = tolower( buff[0] );
1137 		setThirst( getThirst() + level );
1138 		snprintf( msg, MSG_SIZE, _( "%1$s drinks from %2$s." ), getName(), buff );
1139 		session->getGameAdapter()->writeLogMessage( msg, Constants::MSGTYPE_PLAYERITEM );
1140 		usePotion( item );
1141 		bool b = item->decrementCharges();
1142 		if ( b ) {
1143 			snprintf( msg, MSG_SIZE, _( "%s is used up." ), item->getItemName() );
1144 			session->getGameAdapter()->writeLogMessage( msg );
1145 		}
1146 		return b;
1147 	} else {
1148 		session->getGameAdapter()->writeLogMessage( _( "You cannot eat or drink that!" ) );
1149 		return false;
1150 	}
1151 }
1152 
1153 /// Uses a potion.
1154 
usePotion(Item * item)1155 void Creature::usePotion( Item *item ) {
1156 	// nothing to do?
1157 	if ( item->getRpgItem()->getPotionSkill() == -1 ) return;
1158 
1159 	int n;
1160 	enum {MSG_SIZE = 255 };
1161 	char msg[ MSG_SIZE ];
1162 
1163 	int skill = item->getRpgItem()->getPotionSkill();
1164 	if ( skill < 0 ) {
1165 		switch ( -skill - 2 ) {
1166 		case Constants::HP:
1167 			n = item->getRpgItem()->getPotionPower() + item->getLevel();
1168 			if ( n + getHp() > getMaxHp() )
1169 				n = getMaxHp() - getHp();
1170 			setHp( getHp() + n );
1171 			snprintf( msg, MSG_SIZE, _( "%s heals %d points." ), getName(), n );
1172 			session->getGameAdapter()->writeLogMessage( msg, Constants::MSGTYPE_STATS );
1173 			startEffect( Constants::EFFECT_SWIRL, ( Constants::DAMAGE_DURATION * 4 ) );
1174 			return;
1175 		case Constants::MP:
1176 			n = item->getRpgItem()->getPotionPower() + item->getLevel();
1177 			if ( n + getMp() > getMaxMp() )
1178 				n = getMaxMp() - getMp();
1179 			setMp( getMp() + n );
1180 			snprintf( msg, MSG_SIZE, _( "%s receives %d magic points." ), getName(), n );
1181 			session->getGameAdapter()->writeLogMessage( msg, Constants::MSGTYPE_STATS );
1182 			startEffect( Constants::EFFECT_SWIRL, ( Constants::DAMAGE_DURATION * 4 ) );
1183 			return;
1184 		case Constants::AC: {
1185 				bonusArmor += item->getRpgItem()->getPotionPower();
1186 				recalcAggregateValues();
1187 				snprintf( msg, MSG_SIZE, _( "%s feels impervious to damage!" ), getName() );
1188 				session->getGameAdapter()->writeLogMessage( msg, Constants::MSGTYPE_STATS );
1189 				startEffect( Constants::EFFECT_SWIRL, ( Constants::DAMAGE_DURATION * 4 ) );
1190 
1191 				// add calendar event to remove armor bonus
1192 				// (format : sec, min, hours, days, months, years)
1193 				Date d( 0, item->getRpgItem()->getPotionTime() + item->getLevel(), 0, 0, 0, 0 );
1194 				Event *e =
1195 				  new PotionExpirationEvent( session->getParty()->getCalendar()->getCurrentDate(),
1196 				                             d, this, item, session, 1 );
1197 				session->getParty()->getCalendar()->scheduleEvent( ( Event* )e );   // It's important to cast!!
1198 			}
1199 			return;
1200 		default:
1201 			cerr << "Implement me! (other potion skill boost)" << endl;
1202 			return;
1203 		}
1204 	} else {
1205 		skillBonus[skill] += item->getRpgItem()->getPotionPower();
1206 		// recalcAggregateValues();
1207 		snprintf( msg, MSG_SIZE, _( "%s feels at peace." ), getName() );
1208 		session->getGameAdapter()->writeLogMessage( msg, Constants::MSGTYPE_STATS );
1209 		startEffect( Constants::EFFECT_SWIRL, ( Constants::DAMAGE_DURATION * 4 ) );
1210 
1211 		// add calendar event to remove armor bonus
1212 		// (format : sec, min, hours, days, months, years)
1213 		Date d( 0, item->getRpgItem()->getPotionTime() + item->getLevel(), 0, 0, 0, 0 );
1214 		Event *e = new PotionExpirationEvent( session->getParty()->getCalendar()->getCurrentDate(), d, this, item, session, 1 );
1215 		session->getParty()->getCalendar()->scheduleEvent( ( Event* )e );   // It's important to cast!!
1216 	}
1217 }
1218 
1219 /// Set the creature's next action (carried out at begin of next battle turn).
1220 
setAction(int action,Item * item,Spell * spell,SpecialSkill * skill)1221 void Creature::setAction( int action, Item *item, Spell *spell, SpecialSkill *skill ) {
1222 	this->action = action;
1223 	this->actionItem = item;
1224 	this->actionSpell = spell;
1225 	this->actionSkill = skill;
1226 	preActionTargetCreature = getTargetCreature();
1227 	// zero the clock
1228 	setLastTurn( 0 );
1229 
1230 	enum {MSG_SIZE = 80 };
1231 	char msg[ MSG_SIZE ];
1232 	switch ( action ) {
1233 	case Constants::ACTION_EAT_DRINK:
1234 		this->battle->invalidate();
1235 		snprintf( msg, MSG_SIZE, _( "%1$s will consume %2$s." ), getName(), item->getItemName() );
1236 		break;
1237 	case Constants::ACTION_CAST_SPELL:
1238 		this->battle->invalidate();
1239 		snprintf( msg, MSG_SIZE, _( "%1$s will cast %2$s." ), getName(), spell->getDisplayName() );
1240 		break;
1241 	case Constants::ACTION_SPECIAL:
1242 		this->battle->invalidate();
1243 		snprintf( msg, MSG_SIZE, _( "%1$s will use capability %2$s." ), getName(), skill->getDisplayName() );
1244 		break;
1245 	case Constants::ACTION_NO_ACTION:
1246 		// no-op
1247 		preActionTargetCreature = NULL;
1248 		strcpy( msg, "" );
1249 		break;
1250 	default:
1251 		cerr << "*** Error: unknown action " << action << endl;
1252 		return;
1253 	}
1254 
1255 	if ( strlen( msg ) ) {
1256 		if ( session->getParty()->isPartyMember(this) ) {
1257 			session->getGameAdapter()->writeLogMessage( msg, Constants::MSGTYPE_PLAYERBATTLE );
1258 		} else {
1259 			session->getGameAdapter()->writeLogMessage( msg, Constants::MSGTYPE_NPCBATTLE );
1260 		}
1261 	}
1262 }
1263 
1264 /// equip or doff if already equipped
1265 
equipFromBackpack(int backpackIndex,int equipIndexHint)1266 void Creature::equipFromBackpack( int backpackIndex, int equipIndexHint ) {
1267 	this->battle->invalidate();
1268 	// doff
1269 	if ( doff( backpackIndex ) ) return;
1270 	// don
1271 	// FIXME: take into account: two-handed weapons, min skill req-s., etc.
1272 	Item *item = getBackpackItem( backpackIndex );
1273 #ifdef DEBUG_INVENTORY
1274 	cerr << "item at pos " << backpackIndex << " item=" << item << endl;
1275 	debugBackpack();
1276 #endif
1277 
1278 	int place = -1;
1279 	vector<int> places;
1280 	for(int i = 0; i < Constants::EQUIP_LOCATION_COUNT; i++) {
1281 		// if the slot is empty and the item can be worn here
1282 		if ( item->getRpgItem()->getEquip() & ( 1 << i ) &&
1283 		        equipped[i] == MAX_BACKPACK_SIZE ) {
1284 			if ( i == equipIndexHint ) {
1285 				place = i;
1286 				break;
1287 			}
1288 			places.push_back( i );
1289 		}
1290 	}
1291 	if ( place == -1 && !places.empty() ) {
1292 		place = places[ Util::dice(  places.size() ) ];
1293 	}
1294 	if ( place > -1 ) {
1295 
1296 		BackpackInfo *info = getBackpackInfo( item );
1297 		info->equipIndex = place;
1298 
1299 		equipped[ place ] = backpackIndex;
1300 
1301 		// once worn, show if it's cursed
1302 		item->setShowCursed( true );
1303 
1304 		// handle magic attrib settings
1305 		if ( item->isMagicItem() ) {
1306 
1307 			// increase skill bonuses
1308 			map<int, int> *m = item->getSkillBonusMap();
1309 			for ( map<int, int>::iterator e = m->begin(); e != m->end(); ++e ) {
1310 				int skill = e->first;
1311 				int bonus = e->second;
1312 				setSkillBonus( skill, getSkillBonus( skill ) + bonus );
1313 			}
1314 			// if armor, enhance magic resistance
1315 			if ( !item->getRpgItem()->isWeapon() && item->getSchool() ) {
1316 				int skill = item->getSchool()->getResistSkill();
1317 				setSkillBonus( skill, getSkillBonus( skill ) + item->getMagicResistance() );
1318 			}
1319 
1320 		}
1321 
1322 		// recalc current weapon, and the state mods
1323 		recalcAggregateValues();
1324 
1325 		// call script
1326 		if ( isPartyMember() ) session->getSquirrel()->callItemEvent( this, item, "equipItem" );
1327 
1328 		return;
1329 	}
1330 }
1331 
1332 /// Unequips an item.
1333 
doff(int backpackIndex)1334 int Creature::doff( int backpackIndex ) {
1335 	// doff
1336   for(int i = 0; i < Constants::EQUIP_LOCATION_COUNT; i++) {
1337 		if ( equipped[i] == backpackIndex ) {
1338 			Item *item = getBackpackItem( backpackIndex );
1339 			equipped[i] = MAX_BACKPACK_SIZE;
1340 			BackpackInfo *info = getBackpackInfo( item );
1341 			info->equipIndex = -1;
1342 
1343 			// handle magic attrib settings
1344 			if ( item->isMagicItem() ) {
1345 
1346 				// unset the good attributes
1347 				for ( int k = 0; k < StateMod::STATE_MOD_COUNT; k++ ) {
1348 					if ( item->isStateModSet( k ) ) {
1349 						this->setStateMod( k, false );
1350 					}
1351 				}
1352 				// unset the protected attributes
1353 				for ( int k = 0; k < StateMod::STATE_MOD_COUNT; k++ ) {
1354 					if ( item->isStateModProtected( k ) ) {
1355 						this->setProtectedStateMod( k, false );
1356 					}
1357 				}
1358 				// decrease skill bonus
1359 				map<int, int> *m = item->getSkillBonusMap();
1360 				for ( map<int, int>::iterator e = m->begin(); e != m->end(); ++e ) {
1361 					int skill = e->first;
1362 					int bonus = e->second;
1363 					setSkillBonus( skill, getSkillBonus( skill ) - bonus );
1364 				}
1365 				// if armor, reduce magic resistance
1366 				if ( !item->getRpgItem()->isWeapon() && item->getSchool() ) {
1367 					int skill = item->getSchool()->getResistSkill();
1368 					setSkillBonus( skill, getSkillBonus( skill ) - item->getMagicResistance() );
1369 				}
1370 
1371 			}
1372 
1373 			// recalc current weapon, and the state mods
1374 			recalcAggregateValues();
1375 
1376 			// call script
1377 			if ( isPartyMember() ) session->getSquirrel()->callItemEvent( this, item, "doffItem" );
1378 
1379 			return 1;
1380 		}
1381 	}
1382 	return 0;
1383 }
1384 
1385 
1386 /// Get the item at an equip index. (What is at equipped location?)
1387 /// The parameter is an int from 0 - EQUIP_LOCATION_COUNT
getEquippedItemByIndex(int equipIndex)1388 Item *Creature::getEquippedItemByIndex( int equipIndex ) {
1389 	int n = equipped[equipIndex];
1390 	if ( n < MAX_BACKPACK_SIZE ) {
1391 		return getBackpackItem( n );
1392 	}
1393 	return NULL;
1394 }
1395 
1396 
1397 /// Get the item at an equip index. (What is at equipped location?)
1398 /// The parameter is a power of 2 (see constants for EQUIP_LOCATION values
getEquippedItem(int equipLocation)1399 Item *Creature::getEquippedItem( int equipLocation ) {
1400 
1401 	// find out which power of 2 it is
1402 	int equipIndex = Constants::getLocationIndex( equipLocation );
1403 
1404 	return getEquippedItemByIndex( equipIndex );
1405 }
1406 
1407 /// Returns whether the item at an equip index is a weapon.
1408 /// The parameter is a power of 2 (see constants for EQUIP_LOCATION values
1409 
isEquippedWeapon(int equipLocation)1410 bool Creature::isEquippedWeapon( int equipLocation ) {
1411 	Item *item = getEquippedItem( equipLocation );
1412 	return( item && item->getRpgItem()->isWeapon() );
1413 }
1414 
1415 
1416 /// Returns whether the creature has an item currently equipped.
1417 
isEquipped(Item * item)1418 bool Creature::isEquipped( Item *item ) {
1419 	BackpackInfo *info = getBackpackInfo( item );
1420 	return( info && info->equipIndex > -1 );
1421 	/*
1422   for(int i = 0; i < Constants::EQUIP_LOCATION_COUNT; i++) {
1423 	   if(equipped[i] < MAX_BACKPACK_SIZE &&
1424 	      backpack[ equipped[i] ] == item ) return true;
1425 	 }
1426 	 return false;
1427 	*/
1428 }
1429 
1430 /// Returns whether an item at an backpack location is currently equipped somewhere.
1431 
isEquipped(int backpackIndex)1432 bool Creature::isEquipped( int backpackIndex ) {
1433 	if ( backpackIndex < 0 || backpackIndex >= backpack->getContainedItemCount() ) return false;
1434 	return isEquipped( backpack->getContainedItem( backpackIndex ) );
1435 	/*
1436   for(int i = 0; i < Constants::EQUIP_LOCATION_COUNT; i++) {
1437 	   if( equipped[i] == backpackIndex ) return true;
1438 	 }
1439 	 return false;
1440 	*/
1441 }
1442 
1443 /// Unequips cursed items.
1444 
removeCursedItems()1445 bool Creature::removeCursedItems() {
1446 	bool found = false;
1447   for(int i = 0; i < Constants::EQUIP_LOCATION_COUNT; i++) {
1448 		if ( equipped[i] < MAX_BACKPACK_SIZE && backpack->getContainedItem( equipped[i] )->isCursed() ) {
1449 			found = true;
1450 			// not the most efficient way to do this, but it works...
1451 			doff( equipped[i] );
1452 		}
1453 	}
1454 	return found;
1455 }
1456 
1457 /// Gets the equip index of the item stored at an backpack index. (Where is the item worn?)
1458 
getEquippedIndex(int backpackIndex)1459 int Creature::getEquippedIndex( int backpackIndex ) {
1460 	if ( backpackIndex < 0 || backpackIndex >= backpack->getContainedItemCount() ) return -1;
1461 	BackpackInfo *info = getBackpackInfo( backpack->getContainedItem( backpackIndex ) );
1462 	return info->equipIndex;
1463 	/*
1464   for(int i = 0; i < Constants::EQUIP_LOCATION_COUNT; i++) {
1465 	   if(equipped[i] == index) return i;
1466 	 }
1467 	 return -1;
1468 	*/
1469 }
1470 
1471 /// Returns whether an item is worn in the backpack (also recurses containers).
1472 
isItemInBackpack(Item * item)1473 bool Creature::isItemInBackpack( Item *item ) {
1474 	// -=K=-: reverting that back; carried container contents get deleted in Session::cleanUpAfterMission otherwise
1475 	// return( getBackpackInfo( item ) ? true : false );
1476 
1477 	for ( int i = 0; i < backpack->getContainedItemCount(); i++ ) {
1478 		if ( backpack->getContainedItem( i ) == item || ( backpack->getContainedItem( i )->getRpgItem()->getType() == RpgItem::CONTAINER &&
1479 				backpack->getContainedItem( i )->isContainedItem( item ) ) )
1480 			return true;
1481 	}
1482 	return false;
1483 
1484 }
1485 
1486 /// Calculates the aggregate values based on equipped items.
1487 
recalcAggregateValues()1488 void Creature::recalcAggregateValues() {
1489 	armorChanged = true;
1490 
1491 	// try to select a new preferred weapon if needed.
1492 	if ( preferredWeapon == -1 || !isEquippedWeapon( preferredWeapon ) ) {
1493 		int values[] = {
1494       Constants::EQUIP_LOCATION_LEFT_HAND,
1495       Constants::EQUIP_LOCATION_RIGHT_HAND,
1496       Constants::EQUIP_LOCATION_WEAPON_RANGED,
1497 			-1
1498 		};
1499 		preferredWeapon = -1;
1500 		for ( int i = 0; values[ i ] > -1; i++ ) {
1501 			if ( isEquippedWeapon( values[ i ] ) ) {
1502 				preferredWeapon = values[ i ];
1503 				break;
1504 			}
1505 		}
1506 	}
1507 
1508 	for(int i = 0; i < Constants::EQUIP_LOCATION_COUNT; i++) {
1509 		Item *item = getEquippedItemByIndex( i );
1510 		// handle magic attrib settings
1511 		if ( item != NULL && item->isMagicItem() ) {
1512 
1513 			// set the good attributes
1514 			for ( int k = 0; k < StateMod::STATE_MOD_COUNT; k++ ) {
1515 				if ( item->isStateModSet( k ) ) {
1516 					this->setStateMod( k, true );
1517 				}
1518 			}
1519 			// set the protected attributes
1520 			for ( int k = 0; k < StateMod::STATE_MOD_COUNT; k++ ) {
1521 				if ( item->isStateModProtected( k ) ) {
1522 					this->setProtectedStateMod( k, true );
1523 				}
1524 			}
1525 			// refresh map for invisibility, etc.
1526 			session->getMap()->refresh();
1527 		}
1528 	}
1529 }
1530 
1531 /// Selects the next equipped weapon as the active weapon.
1532 
nextPreferredWeapon()1533 bool Creature::nextPreferredWeapon() {
1534 #ifdef DEBUG_INVENTORY
1535 	cerr << "nextPreferredWeapon" << endl;
1536 	debugBackpack();
1537 #endif
1538 	int pos = preferredWeapon;
1539 	for ( int i = 0; i < 4; i++ ) {
1540 		switch ( pos ) {
1541     case Constants::EQUIP_LOCATION_LEFT_HAND: pos = Constants::EQUIP_LOCATION_RIGHT_HAND; break;
1542     case Constants::EQUIP_LOCATION_RIGHT_HAND: pos = Constants::EQUIP_LOCATION_WEAPON_RANGED; break;
1543     case Constants::EQUIP_LOCATION_WEAPON_RANGED: pos = -1; break;
1544     case -1: pos = Constants::EQUIP_LOCATION_LEFT_HAND; break;
1545 		}
1546 #ifdef DEBUG_INVENTORY
1547 		if( this == session->getParty()->getPlayer() ) {
1548 			if( pos != -1 ) {
1549 				cerr << "\tchecking pos=" << pos << " equipped=" << getEquippedItem( pos ) << " weapon: " << isEquippedWeapon( pos ) << endl;
1550 			}
1551 		}
1552 #endif
1553 		if ( pos == -1 || isEquippedWeapon( pos ) ) {
1554 			preferredWeapon = pos;
1555 			return true;
1556 		}
1557 	}
1558 	preferredWeapon = -1;
1559 	return false;
1560 }
1561 
1562 /// Returns an equipped weapon with an action radius >= dist, or NULL otherwise.
1563 
getBestWeapon(float dist,bool callScript)1564 Item *Creature::getBestWeapon( float dist, bool callScript ) {
1565 
1566 	Item *ret = NULL;
1567 
1568 	// for TB combat for players, respect the current weapon
1569 	if ( session->getPreferences()->isBattleTurnBased() && isPartyMember() ) {
1570 		ret = ( preferredWeapon > -1 ? getEquippedItem( preferredWeapon ) : NULL );
1571 	} else {
1572 		int location[] = {
1573       Constants::EQUIP_LOCATION_RIGHT_HAND,
1574       Constants::EQUIP_LOCATION_LEFT_HAND,
1575       Constants::EQUIP_LOCATION_WEAPON_RANGED,
1576 			-1
1577 		};
1578 		for ( int i = 0; location[i] > -1; i++ ) {
1579 			Item *item = getEquippedItem( location[i] );
1580 			if ( item &&
1581 			        item->getRpgItem()->isWeapon() &&
1582 			        item->getRange() >= dist ) {
1583 				ret = item;
1584 				break;
1585 			}
1586 		}
1587 	}
1588 	if ( isPartyMember() && ret && callScript && SQUIRREL_ENABLED ) {
1589 		session->getSquirrel()->callItemEvent( this, ret, "startBattleWithItem" );
1590 	}
1591 
1592 	return ret;
1593 }
1594 
1595 /// Returns the initiative for a battle round, the higher, the faster the attack.
1596 
getInitiative(int * max)1597 int Creature::getInitiative( int *max ) {
1598 	float n = ( getSkill( Skill::SPEED ) + ( getSkill( Skill::LUCK ) / 5.0f ) );
1599 	if ( max ) *max = toint( n );
1600 	return toint( Util::roll( 0.0f, n ) );
1601 }
1602 
1603 /// Returns the number of projectiles that can be launched simultaneously.
1604 
1605 /// it is a function of speed, level, and weapon skill
1606 /// this method returns a number from 1-10
1607 
getMaxProjectileCount(Item * item)1608 int Creature::getMaxProjectileCount( Item *item ) {
1609 	int n = static_cast<int>( static_cast<double>( getSkill( Skill::SPEED ) + ( getLevel() * 10 ) +
1610 	                          getSkill( item->getRpgItem()->getDamageSkill() ) ) / 30.0f );
1611 	if ( n <= 0 )
1612 		n = 1;
1613 	return n;
1614 }
1615 
1616 /// Returns the projectiles that have been fired by the creature.
1617 
getProjectiles()1618 vector<RenderedProjectile*> *Creature::getProjectiles() {
1619 	map<RenderedCreature*, vector<RenderedProjectile*>*> *m = RenderedProjectile::getProjectileMap();
1620 	return( m->find( this ) == m->end() ? NULL : ( *m )[ ( RenderedCreature* )this ] );
1621 }
1622 
1623 /// Take some damage and show a nice damage effect. Return true if the creature is killed.
1624 
takeDamage(float damage,int effect_type,GLuint delay)1625 bool Creature::takeDamage( float damage,
1626                            int effect_type,
1627                            GLuint delay ) {
1628 
1629 	int intDamage = toint( damage );
1630 	addRecentDamage( intDamage );
1631 
1632 	hp -= intDamage;
1633 	// if creature dies start effect at its location
1634 	if ( hp > 0 ) {
1635 		startEffect( effect_type );
1636 		int pain = Util::dice(  3 );
1637 		getShape()->setCurrentAnimation( pain == 0 ? static_cast<int>( MD2_PAIN1 ) : ( pain == 1 ? static_cast<int>( MD2_PAIN2 ) : static_cast<int>( MD2_PAIN3 ) ) );
1638 	} else if ( effect_type != Constants::EFFECT_GLOW ) {
1639 		session->getMap()->startEffect( toint( getX() ), toint( getY() - this->getShape()->getDepth() + 1 ), toint( getZ() ),
1640 		    effect_type, ( Constants::DAMAGE_DURATION * 4 ),
1641 		    getShape()->getWidth(), getShape()->getDepth(), delay );
1642 	}
1643 
1644 	// creature death here so it can be used from script
1645 	if ( hp <= 0 ) {
1646 		if ( !( ( isMonster() && MONSTER_IMORTALITY ) || ( isPartyMember() && GOD_MODE ) ) ) {
1647 			session->creatureDeath( this );
1648 		}
1649 		return true;
1650 	} else {
1651 		return false;
1652 	}
1653 }
1654 
1655 /// Raises the creature from the dead.
1656 
resurrect(int rx,int ry)1657 void Creature::resurrect( int rx, int ry ) {
1658 	// remove all state mod effects
1659 	for ( int i = 0; i < StateMod::STATE_MOD_COUNT; i++ ) {
1660 		setStateMod( i, false );
1661 	}
1662 	if ( getThirst() < 5 ) setThirst( 5 );
1663 	if ( getHunger() < 5 ) setHunger( 5 );
1664 
1665 	setHp( Util::pickOne(  1, 3 ) );
1666 
1667 	findPlace( rx, ry );
1668 
1669 	startEffect( Constants::EFFECT_TELEPORT, ( Constants::DAMAGE_DURATION * 4 ) );
1670 
1671 	char msg[120];
1672 	snprintf( msg, 120, _( "%s is raised from the dead!" ), getName() );
1673 	session->getGameAdapter()->writeLogMessage( msg, Constants::MSGTYPE_STATS );
1674 }
1675 
1676 /// Gives experience points for a creature kill.
1677 
1678 /// add exp after killing a creature
1679 /// only called for characters
1680 
addExperience(Creature * creature_killed)1681 int Creature::addExperience( Creature *creature_killed ) {
1682 	int n = ( creature_killed->level + 1 ) * 25;
1683 	// extra for killing higher level creatures
1684 	int bonus = ( creature_killed->level - getLevel() );
1685 	if ( bonus > 0 ) n += bonus * 10;
1686 	return addExperienceWithMessage( n );
1687 }
1688 
1689 /// Gives experience points.
1690 
1691 /// Add n exp points. Only called for characters
1692 /// Note that n can be a negative number. (eg.: failure to steal)
1693 
addExperience(int delta)1694 int Creature::addExperience( int delta ) {
1695 	int n = delta;
1696 	experience += n;
1697 	if ( experience < 0 ) {
1698 		n = experience;
1699 		experience = 0;
1700 	}
1701 
1702 	// level up?
1703 	if ( experience >= expOfNextLevel ) {
1704 		level++;
1705 		hp += getStartingHp();
1706 		mp += getStartingMp();
1707 		calculateExpOfNextLevel();
1708 		setAvailableSkillMod( getAvailableSkillMod() + character->getSkillBonus() );
1709 		char message[255];
1710 		snprintf( message, 255, _( "  %s levels up!" ), getName() );
1711 		session->getGameAdapter()->startTextEffect( message );
1712 		session->getGameAdapter()->refreshBackpackUI();
1713 	}
1714 
1715 	evalSpecialSkills();
1716 
1717 	return n;
1718 }
1719 
1720 /// Gives experience points and adds an appropriate message to the log scroller.
1721 
1722 /// Add experience and show message in map window. Also shows
1723 /// message if creature leveled up. Use generally for party
1724 /// characters only.
1725 
addExperienceWithMessage(int exp)1726 int Creature::addExperienceWithMessage( int exp ) {
1727 	int n = 0;
1728 	if ( !getStateMod( StateMod::dead ) ) {
1729 		enum { MSG_SIZE = 120 };
1730 		char message[ MSG_SIZE ];
1731 		int oldLevel = level;
1732 		n = addExperience( exp );
1733 		if ( n > 0 ) {
1734 			snprintf( message, MSG_SIZE, _( "%s gains %d experience points." ), getName(), n );
1735 			session->getGameAdapter()->writeLogMessage( message, Constants::MSGTYPE_STATS );
1736 			if ( oldLevel != level ) {
1737 				snprintf( message, MSG_SIZE, _( "%s gains a level!" ), getName() );
1738 				session->getGameAdapter()->writeLogMessage( message, Constants::MSGTYPE_MISSION );
1739 			}
1740 		} else if ( n < 0 ) {
1741 			snprintf( message, MSG_SIZE, _( "%s looses %d experience points!" ), getName(), -n );
1742 			session->getGameAdapter()->writeLogMessage( message, Constants::MSGTYPE_STATS );
1743 		}
1744 	}
1745 	return n;
1746 }
1747 
1748 /// Adds money after a creature kill
1749 
addMoney(Creature * creature_killed)1750 int Creature::addMoney( Creature *creature_killed ) {
1751 	int n = creature_killed->level - getLevel();
1752 	if ( n < 1 ) n = 1;
1753 	// fixme: use creature_killed->getMonster()->getMoney() instead of 100.0f
1754 	long delta = ( long )n * Util::dice( 50 );
1755 	money += delta;
1756 	return delta;
1757 }
1758 
1759 
1760 /// Returns the angle between the creature and its target.
1761 
getTargetAngle()1762 float Creature::getTargetAngle() {
1763 	//if(!targetCreature) return -1.0f;
1764 	if ( !targetCreature ) return angle;
1765 	return Util::getAngle( getX(), getY(), getShape()->getWidth(), getShape()->getDepth(),
1766 	                       getTargetCreature()->getX(), getTargetCreature()->getY(),
1767 	                       getTargetCreature()->getShape()->getWidth(),
1768 	                       getTargetCreature()->getShape()->getHeight() );
1769 }
1770 
1771 /// Returns whether the creature knows a specific spell.
1772 
1773 // FIXME: O(n) but there aren't that many spells...
isSpellMemorized(Spell * spell)1774 bool Creature::isSpellMemorized( Spell *spell ) {
1775 	for ( int i = 0; i < static_cast<int>( spells.size() ); i++ ) {
1776 		if ( spells[i] == spell ) return true;
1777 	}
1778 	return false;
1779 }
1780 
1781 /// Adds a spell to the creature.
1782 
1783 /// FIXME: O(n) but there aren't that many spells...
1784 /// return true if spell was added, false if creature already had this spell
1785 
addSpell(Spell * spell)1786 bool Creature::addSpell( Spell *spell ) {
1787 	for ( vector<Spell*>::iterator e = spells.begin(); e != spells.end(); ++e ) {
1788 		Spell *thisSpell = *e;
1789 		if ( thisSpell == spell ) return false;
1790 	}
1791 	spells.push_back( spell );
1792 	return true;
1793 }
1794 
1795 // this assumes that hasTarget() was called first.
isTargetValid()1796 bool Creature::isTargetValid() {
1797 	// is it a non-creature target? (item or location)
1798 	if ( !getTargetCreature() ) return true;
1799 	if ( getTargetCreature()->getStateMod( StateMod::dead ) ) return false;
1800 	// when attacking, attack the opposite kind (unless possessed)
1801 	// however, you can cast spells on anyone
1802 	if ( getAction() == Constants::ACTION_NO_ACTION &&
1803 	        !canAttack( getTargetCreature() ) ) return false;
1804 	return true;
1805 }
1806 
1807 /// Returns whether the creature can attack the other creature, and the mouse cursor the game should display.
1808 
canAttack(RenderedCreature * creature,int * cursor)1809 bool Creature::canAttack( RenderedCreature *creature, int *cursor ) {
1810 	// when attacking, attack the opposite kind (unless possessed)
1811 	bool ret;
1812 	if ( isMonster() ) {
1813 		if ( !getStateMod( StateMod::possessed ) ) {
1814 			ret = ( ( !creature->isMonster() && !creature->getStateMod( StateMod::possessed ) ) ||
1815 			        ( creature->isMonster() && creature->getStateMod( StateMod::possessed ) ) );
1816 		} else {
1817 			ret = ( ( !creature->isMonster() && creature->getStateMod( StateMod::possessed ) ) ||
1818 			        ( creature->isMonster() && !creature->getStateMod( StateMod::possessed ) ) );
1819 		}
1820 	} else {
1821 		if ( !getStateMod( StateMod::possessed ) ) {
1822 			ret = ( ( creature->isMonster() && !creature->getStateMod( StateMod::possessed ) ) ||
1823 			        ( !creature->isMonster() && creature->getStateMod( StateMod::possessed ) ) );
1824 		} else {
1825 			ret = ( ( !creature->isMonster() && !creature->getStateMod( StateMod::possessed ) ) ||
1826 			        ( creature->isMonster() && creature->getStateMod( StateMod::possessed ) ) );
1827 		}
1828 	}
1829 	if ( ret && cursor ) {
1830 		float dist = getDistanceToTarget( creature );
1831 		Item *item = getBestWeapon( dist );
1832 		if ( dist <= getBattle()->calculateRange( item ) ) {
1833 			*cursor = ( !item ? Constants::CURSOR_NORMAL :
1834 			            ( item->getRpgItem()->isRangedWeapon() ?
1835 			              Constants::CURSOR_RANGED :
1836 			              Constants::CURSOR_ATTACK ) );
1837 		} else {
1838 			*cursor = Constants::CURSOR_MOVE;
1839 		}
1840 	}
1841 	return ret;
1842 }
1843 
1844 /// Cancels the creature's target selection.
1845 
cancelTarget()1846 void Creature::cancelTarget() {
1847 	setTargetCreature( NULL );
1848 	setTargetItem( 0, 0, 0, NULL );
1849 	if ( preActionTargetCreature ) setTargetCreature( preActionTargetCreature );
1850 	preActionTargetCreature = NULL;
1851 	setAction( Constants::ACTION_NO_ACTION );
1852 	if ( !isPartyMember() && !scripted ) {
1853 		setMotion( Constants::MOTION_LOITER );
1854 		pathManager->findWanderingPath( CREATURE_LOITERING_RADIUS, session->getParty()->getPlayer(), session->getMap() );
1855 	}
1856 }
1857 
1858 /// Does the item's prerequisite apply to this creature?
1859 
isWithPrereq(Item * item)1860 bool Creature::isWithPrereq ( Item *item ) {
1861   // If the item is non-magical or used up, get out.
1862   if ( !item->isMagicItem() || ( item->getCurrentCharges() < 1 ) ) return false;
1863 
1864   // Is it a potion?
1865   if ( item->getRpgItem()->getPotionSkill() < 0 ) {
1866     int potionSkill = ( ( item->getRpgItem()->getPotionSkill() < 0 ) ? ( -item->getRpgItem()->getPotionSkill() - 2 ) : 0 ) ;
1867     float remainingHP = getHp() / ( getMaxHp() ? getMaxHp() : 1 );
1868     float remainingMP = getMp() / ( getMaxMp() ? getMaxMp() : 1 );
1869     if ( ( potionSkill == Constants::HP ) && ( remainingHP <= LOW_HP ) ) return true;
1870     if ( ( potionSkill == Constants::MP ) && ( ( remainingMP <= LOW_MP ) && getMaxMp() ) ) return true;
1871   }
1872 
1873   // Is it an item holding a spell?
1874   if ( item->getSpell() ) {
1875     if ( isWithPrereq( item->getSpell() ) ) return true;
1876   }
1877 
1878 return false;
1879 }
1880 
1881 /// Does the spell's prerequisite apply to this creature?
1882 
isWithPrereq(Spell * spell)1883 bool Creature::isWithPrereq( Spell *spell ) {
1884 	if ( spell->isStateModPrereqAPotionSkill() ) {
1885 		switch ( spell->getStateModPrereq() ) {
1886 		case Constants::HP:
1887 			//cerr << "\tisWithPrereq: " << getName() << " max hp=" << getMaxHp() << " hp=" << getHp() << endl;
1888 			return( getHp() <= static_cast<int>( static_cast<float>( getMaxHp() ) * LOW_HP ) );
1889 		case Constants::MP:
1890 			return( getMp() <= static_cast<int>( static_cast<float>( getMaxMp() ) * LOW_MP ) );
1891 		case Constants::AC:
1892 			/*
1893 			FIXME: Even if needed only cast it 1 out of 4 times.
1894 			Really need some AI here to remember if the spell helped or not. (or some way
1895 			to predict if casting a spell will help.) Otherwise the monster keeps casting
1896 			Body of Stone to no effect.
1897 			Also: HIGH_AC should not be hard-coded...
1898 			*/
1899 			float armor, dodgePenalty;
1900 			getArmor( &armor, &dodgePenalty, 0 );
1901 			return( armor >= HIGH_AC ? false
1902 			        : ( Util::dice( 4 ) == 0 ? true
1903 			            : false ) );
1904 		default: return false;
1905 		}
1906 	} else {
1907 		return getStateMod( spell->getStateModPrereq() );
1908 	}
1909 }
1910 
1911 /// Finds the closest possible target creature for a spell.
1912 
findClosestTargetWithPrereq(Spell * spell)1913 Creature *Creature::findClosestTargetWithPrereq( Spell *spell ) {
1914 
1915 	// is it self?
1916 	if ( isWithPrereq( spell ) ) return this;
1917 
1918 	// who are the possible targets?
1919 	vector<Creature*> possibleTargets;
1920 	if ( getStateMod( StateMod::possessed ) ) {
1921 		if ( !isMonster() ) {
1922 			for ( int i = 0; i < session->getParty()->getPartySize(); i++ ) {
1923 				if ( !session->getParty()->getParty( i )->getStateMod( StateMod::dead ) && session->getMap()->isLocationVisible( toint( session->getParty()->getParty( i )->getX() ), toint( session->getParty()->getParty( i )->getY() ) ) && session->getMap()->isLocationInLight( toint( session->getParty()->getParty( i )->getX() ), toint( session->getParty()->getParty( i )->getY() ), session->getParty()->getParty( i )->getShape() ) &&  session->getParty()->getParty( i )->isWithPrereq( spell ) )
1924 				possibleTargets.push_back( session->getParty()->getParty( i ) );
1925 			}
1926 		}
1927 		for ( int i = 0; i < session->getCreatureCount(); i++ ) {
1928 			if ( ( !isMonster() ? session->getCreature( i )->isMonster() : ( session->getCreature( i )->isNpc() || session->getCreature( i )->isWanderingHero() ) ) && !session->getCreature( i )->getStateMod( StateMod::dead ) && session->getMap()->isLocationVisible( toint( session->getCreature( i )->getX() ), toint( session->getCreature( i )->getY() ) ) && session->getMap()->isLocationInLight( toint( session->getCreature( i )->getX() ), toint( session->getCreature( i )->getY() ), session->getCreature( i )->getShape() ) &&  session->getCreature( i )->isWithPrereq( spell ) )
1929 			possibleTargets.push_back( session->getCreature( i ) );
1930 		}
1931 	} else {
1932 		for ( int i = 0; i < session->getCreatureCount(); i++ ) {
1933 			if ( ( isMonster() ? session->getCreature( i )->isMonster() : ( session->getCreature( i )->isNpc() || session->getCreature( i )->isWanderingHero() ) ) && !session->getCreature( i )->getStateMod( StateMod::dead ) && session->getMap()->isLocationVisible( toint( session->getCreature( i )->getX() ), toint( session->getCreature( i )->getY() ) ) && session->getMap()->isLocationInLight( toint( session->getCreature( i )->getX() ), toint( session->getCreature( i )->getY() ), session->getCreature( i )->getShape() ) &&  session->getCreature( i )->isWithPrereq( spell ) )
1934 			possibleTargets.push_back( session->getCreature( i ) );
1935 		}
1936 	}
1937 
1938 	// find the closest one that is closer than 20 spaces away.
1939 	Creature *closest = NULL;
1940 	float closestDist = 0;
1941 	for ( int i = 0; i < static_cast<int>( possibleTargets.size() ); i++ ) {
1942 		Creature *p = possibleTargets[ i ];
1943 		float dist =
1944 		  Constants::distance( getX(),  getY(),
1945 		                       getShape()->getWidth(), getShape()->getDepth(),
1946 		                       p->getX(), p->getY(),
1947 		                       p->getShape()->getWidth(),
1948 		                       p->getShape()->getDepth() );
1949 		if ( !closest || dist < closestDist ) {
1950 			closest = p;
1951 			closestDist = dist;
1952 		}
1953 	}
1954 	return( closest && closestDist < (float)CREATURE_SIGHT_RADIUS ? closest : NULL );
1955 }
1956 
1957 /// Basic NPC/monster AI.
1958 
decideAction()1959 void Creature::decideAction() {
1960   Uint32 now = SDL_GetTicks();
1961   if( now - lastDecision < AI_DECISION_INTERVAL ) return;
1962   lastDecision = now;
1963 
1964   if ( scripted ) {
1965     getShape()->setCurrentAnimation( getScriptedAnimation() );
1966     return;
1967   }
1968 
1969   // override from squirrel
1970   if( SQUIRREL_ENABLED ) {
1971     HSQOBJECT *cref = session->getSquirrel()->getCreatureRef( this );
1972     if ( cref ) {
1973       bool result;
1974       session->getSquirrel()->callBoolMethod( "decideAction", cref, &result );
1975       if ( result ) return;
1976     }
1977   }
1978 
1979   // This is the AI's decision matrix. On every decision cycle, it is walked
1980   // top to bottom and collects the max weights between all rows (states) that
1981   // currently apply to the situation. The result is an array of
1982   // (AI_ACTION_COUNT) weights which are then normalized. A dice is thrown
1983   // against random indices until the roll is <= the weight saved there. The
1984   // associated action is then executed.
1985 
1986   // For the sake of consistency, the sum of the weights in a row should
1987   // always be 1.
1988 
1989   // Actions (from left to right):
1990   // AtkClosest, Cast, AreaCast, Heal, ACCast, AtkRandom, StartLoiter, StopMoving, GoOn
1991   float decisionMatrix[ AI_STATE_COUNT ][ AI_ACTION_COUNT ] = {
1992     { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.25f, 0.0f, 0.75f }, // Hanging around, no enemies
1993     { 0.8f, 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, 0.0f, 0.0f, 0.0f }, // Hanging around, enemies near
1994     { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, 0.8f }, // Loitering, no enemies
1995     { 0.8f, 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, 0.0f, 0.0f, 0.0f }, // Loitering, enemies near
1996     { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, 0.8f, 0.0f }, // Loitering, end of path
1997     { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, 0.0f, 0.0f, 0.8f }, // Moving towards target
1998     { 0.0f, 0.3f, 0.1f, 0.0f, 0.0f, 0.2f, 0.0f, 0.0f, 0.4f }, // Engaging target
1999     { 0.0f, 0.0f, 0.0f, 0.8f, 0.0f, 0.2f, 0.0f, 0.0f, 0.0f }, // Low HP
2000     { 0.0f, 0.35f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.65f }, // Target has low HP
2001     { 0.0f, 0.0f, 0.0f, 0.8f, 0.0f, 0.1f, 0.0f, 0.0f, 0.1f }, // Low MP
2002     { 0.0f, 0.35f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.65f }, // Target has low MP
2003     { 0.0f, 0.0f, 0.0f, 0.0f, 0.25f, 0.0f, 0.0f, 0.0f, 0.75f }, // No nice AC boost
2004     { 0.0f, 0.1f, 0.5f, 0.0f, 0.2f, 0.2f, 0.0f, 0.0f, 0.0f }, // Surrounded (min. 3 attackers)
2005     { 0.0f, 0.4f, 0.25f, 0.0f, 0.2f, 0.15f, 0.0f, 0.0f, 0.0f }, // Friendlies outnumbered by enemy
2006     { 0.0f, 0.2f, 0.15f, 0.0f, 0.1f, 0.2f, 0.0f, 0.0f, 0.35f } // Friendlies outnumbering enemy
2007   };
2008 
2009   float decisionWeights[ AI_ACTION_COUNT ];
2010   for ( int i = 0; i < AI_ACTION_COUNT; i++ ) {
2011     decisionWeights[ i ] = 0.0f;
2012   }
2013 
2014   // STEP 1: Collect the weights of the active states.
2015 
2016   // Collect the standing and loitering states.
2017 
2018   Creature *closestTarget = getClosestTarget();
2019 
2020   if ( ( getMotion() == Constants::MOTION_STAND ) && !closestTarget ) {
2021     for ( int i = 0; i < AI_ACTION_COUNT; i++ ) {
2022       if ( decisionMatrix[ AI_STATE_STANDING_NO_ENEMY ][ i ] > decisionWeights[ i ] ) decisionWeights[ i ] = decisionMatrix[ AI_STATE_STANDING_NO_ENEMY ][ i ];
2023     }
2024   } else if ( ( getMotion() == Constants::MOTION_STAND ) && closestTarget && !hasTarget() ) {
2025     for ( int i = 0; i < AI_ACTION_COUNT; i++ ) {
2026       if ( decisionMatrix[ AI_STATE_STANDING_ENEMY_AROUND ][ i ] > decisionWeights[ i ] ) decisionWeights[ i ] = decisionMatrix[ AI_STATE_STANDING_ENEMY_AROUND ][ i ];
2027     }
2028   } else if ( ( getMotion() == Constants::MOTION_LOITER ) && !closestTarget && !getPathManager()->atEndOfPath() ) {
2029     for ( int i = 0; i < AI_ACTION_COUNT; i++ ) {
2030       if ( decisionMatrix[ AI_STATE_LOITERING_NO_ENEMY ][ i ] > decisionWeights[ i ] ) decisionWeights[ i ] = decisionMatrix[ AI_STATE_LOITERING_NO_ENEMY ][ i ];
2031     }
2032   } else if ( ( getMotion() == Constants::MOTION_LOITER ) && closestTarget && !hasTarget() ) {
2033     for ( int i = 0; i < AI_ACTION_COUNT; i++ ) {
2034       if ( decisionMatrix[ AI_STATE_LOITERING_ENEMY_AROUND ][ i ] > decisionWeights[ i ] ) decisionWeights[ i ] = decisionMatrix[ AI_STATE_LOITERING_ENEMY_AROUND ][ i ];
2035     }
2036   } else if ( ( getMotion() == Constants::MOTION_LOITER ) && getPathManager()->atEndOfPath() ) {
2037     for ( int i = 0; i < AI_ACTION_COUNT; i++ ) {
2038       if ( decisionMatrix[ AI_STATE_LOITERING_END_OF_PATH ][ i ] > decisionWeights[ i ] ) decisionWeights[ i ] = decisionMatrix[ AI_STATE_LOITERING_END_OF_PATH ][ i ];
2039     }
2040   } else if ( ( getMotion() == Constants::MOTION_MOVE_TOWARDS ) && hasTarget() ) {
2041     for ( int i = 0; i < AI_ACTION_COUNT; i++ ) {
2042       if ( decisionMatrix[ AI_STATE_MOVING_TOWARDS_ENEMY ][ i ] > decisionWeights[ i ] ) decisionWeights[ i ] = decisionMatrix[ AI_STATE_MOVING_TOWARDS_ENEMY ][ i ];
2043     }
2044   } else if ( ( getMotion() == Constants::MOTION_STAND ) && hasTarget() ) {
2045     for ( int i = 0; i < AI_ACTION_COUNT; i++ ) {
2046       if ( decisionMatrix[ AI_STATE_ENGAGING_ENEMY ][ i ] > decisionWeights[ i ] ) decisionWeights[ i ] = decisionMatrix[ AI_STATE_ENGAGING_ENEMY ][ i ];
2047     }
2048   }
2049 
2050   // Collect the HP/MP states.
2051 
2052   float remainingHP = getHp() / ( getMaxHp() ? getMaxHp() : 1 );
2053 
2054   if ( remainingHP <= LOW_HP ) {
2055     for ( int i = 0; i < AI_ACTION_COUNT; i++ ) {
2056       if ( decisionMatrix[ AI_STATE_LOW_HP ][ i ] > decisionWeights[ i ] ) decisionWeights[ i ] = decisionMatrix[ AI_STATE_LOW_HP ][ i ];
2057     }
2058   }
2059 
2060   if ( getTargetCreature() ) {
2061    remainingHP = getTargetCreature()->getHp() / ( getTargetCreature()->getMaxHp() ? getTargetCreature()->getMaxHp() : 1 );
2062 
2063     if ( remainingHP <= LOW_HP ) {
2064       for ( int i = 0; i < AI_ACTION_COUNT; i++ ) {
2065       if ( decisionMatrix[ AI_STATE_ENEMY_LOW_HP ][ i ] > decisionWeights[ i ] ) decisionWeights[ i ] = decisionMatrix[ AI_STATE_ENEMY_LOW_HP ][ i ];
2066       }
2067     }
2068   }
2069 
2070   float remainingMP = getMp() / ( getMaxMp() ? getMaxMp() : 1 );
2071 
2072   if ( ( remainingMP <= LOW_MP ) && getMaxMp() ) {
2073     for ( int i = 0; i < AI_ACTION_COUNT; i++ ) {
2074       if ( decisionMatrix[ AI_STATE_LOW_MP ][ i ] > decisionWeights[ i ] ) decisionWeights[ i ] = decisionMatrix[ AI_STATE_LOW_MP ][ i ];
2075     }
2076   }
2077 
2078   if ( getTargetCreature() ) {
2079    remainingMP = getTargetCreature()->getMp() / ( getTargetCreature()->getMaxMp() ? getTargetCreature()->getMaxMp() : 1 );
2080 
2081     if ( ( remainingMP <= LOW_MP ) && getTargetCreature()->getMaxMp() ) {
2082       for ( int i = 0; i < AI_ACTION_COUNT; i++ ) {
2083       if ( decisionMatrix[ AI_STATE_ENEMY_LOW_MP ][ i ] > decisionWeights[ i ] ) decisionWeights[ i ] = decisionMatrix[ AI_STATE_ENEMY_LOW_MP ][ i ];
2084       }
2085     }
2086   }
2087 
2088   // Collect the "I love to pimp my armor class" state.
2089 
2090   float armor, dodgePenalty;
2091   getArmor( &armor, &dodgePenalty, 0 );
2092 
2093   if ( armor < HIGH_AC ) {
2094     for ( int i = 0; i < AI_ACTION_COUNT; i++ ) {
2095       if ( decisionMatrix[ AI_STATE_AC_NEEDS_PIMPING ][ i ] > decisionWeights[ i ] ) decisionWeights[ i ] = decisionMatrix[ AI_STATE_AC_NEEDS_PIMPING ][ i ];
2096     }
2097   }
2098 
2099   // Get information for the last 3 states.
2100 
2101   Creature *c;
2102   int numAttackers = 0;
2103   int numFriendlies = 0;
2104   int numFoes = 0;
2105   float dist;
2106 
2107   for ( int i = 0; i < session->getCreatureCount(); i++ ) {
2108     c = session->getCreature( i );
2109 
2110     if ( c->getTargetCreature() == this ) numAttackers++;
2111 
2112     dist = Constants::distance( getX(),  getY(), getShape()->getWidth(), getShape()->getDepth(), c->getX(), c->getY(), c->getShape()->getWidth(), c->getShape()->getDepth() );
2113 
2114     if ( dist <= CREATURE_SIGHT_RADIUS ) {
2115       if ( isFriendly( c ) ) {
2116         numFriendlies++;
2117       } else {
2118         numFoes++;
2119       }
2120     }
2121 
2122   }
2123 
2124   // Collect the "surrounded" state.
2125 
2126   if ( numAttackers > 2 ) {
2127     for ( int i = 0; i < AI_ACTION_COUNT; i++ ) {
2128       if ( decisionMatrix[ AI_STATE_SURROUNDED ][ i ] > decisionWeights[ i ] ) decisionWeights[ i ] = decisionMatrix[ AI_STATE_SURROUNDED ][ i ];
2129     }
2130   }
2131 
2132   // Collect the "outnumbered" states.
2133 
2134   if ( numFoes > ( numFriendlies * 2 ) ) {
2135     for ( int i = 0; i < AI_ACTION_COUNT; i++ ) {
2136       if ( decisionMatrix[ AI_STATE_OUTNUMBERED ][ i ] > decisionWeights[ i ] ) decisionWeights[ i ] = decisionMatrix[ AI_STATE_OUTNUMBERED ][ i ];
2137     }
2138   } else if ( numFriendlies > ( numFoes * 2 ) ) {
2139     for ( int i = 0; i < AI_ACTION_COUNT; i++ ) {
2140       if ( decisionMatrix[ AI_STATE_FEW_ENEMIES ][ i ] > decisionWeights[ i ] ) decisionWeights[ i ] = decisionMatrix[ AI_STATE_FEW_ENEMIES ][ i ];
2141     }
2142   }
2143 
2144   // STEP 2: Process the accumulated weights.
2145 
2146   // Normalize the collected weights so their sum is 1.
2147 
2148   float weightSum = 0.0f;
2149 
2150   for ( int i = 0; i < AI_ACTION_COUNT; i++ ) {
2151     weightSum += decisionWeights[ i ];
2152   }
2153   for ( int i = 0; i < AI_ACTION_COUNT; i++ ) {
2154     decisionWeights[ i ] /= weightSum;
2155   }
2156 
2157   // TODO:Shift the weights towards their average for chaotic creatures.
2158 
2159   // STEP 3: Determine the action to do.
2160 
2161   int action;
2162 
2163   // Throw a dice against randomly picked nonzero weights.
2164   // NOTE: This will result in an endless loop if all weights are zero!
2165 
2166   while ( true ) {
2167     action = Util::pickOne( 0, AI_ACTION_COUNT - 1 );
2168     if ( decisionWeights[ action ] > 0.0f ) {
2169       if ( Util::roll( 0.0f, 1.0f ) <= decisionWeights[ action ] ) break;
2170     }
2171   }
2172 
2173 #ifdef DEBUG_CREATURE_AI
2174   cerr << getName() << " action=" << action << endl;
2175 #endif
2176 
2177   switch ( action ) {
2178     case AI_ACTION_ATTACK_CLOSEST_ENEMY:
2179     	attackClosestTarget();
2180       break;
2181     case AI_ACTION_CAST_ATTACK_SPELL:
2182       castOffensiveSpell();
2183       break;
2184     case AI_ACTION_CAST_AREA_SPELL:
2185       castAreaSpell();
2186       break;
2187     case AI_ACTION_HEAL:
2188       if ( !castHealingSpell() ) useMagicItem();
2189       break;
2190     case AI_ACTION_CAST_AC_SPELL:
2191       castACSpell();
2192       break;
2193     case AI_ACTION_ATTACK_RANDOM_ENEMY:
2194       attackRandomTarget();
2195       break;
2196     case AI_ACTION_START_LOITERING:
2197       pathManager->findWanderingPath( CREATURE_LOITERING_RADIUS, session->getParty()->getPlayer(), session->getMap() );
2198       setMotion( Constants::MOTION_LOITER );
2199       break;
2200     case AI_ACTION_STOP_MOVING:
2201       setMotion( Constants::MOTION_STAND );
2202       stopMoving();
2203       break;
2204     case AI_ACTION_GO_ON: // GoOn (a no-op!)
2205       break;
2206   }
2207 
2208 }
2209 
2210 /// Returns the closest suitable battle target, NULL if no target found.
2211 
getClosestTarget()2212 Creature *Creature::getClosestTarget() {
2213   Creature *p;
2214   bool possessed = getStateMod( StateMod::possessed );
2215 
2216   // Select a suitable target.
2217   if ( isMonster() ) {
2218     p = ( possessed ? session->getClosestMonster ( toint ( getX() ), toint ( getY() ), getShape()->getWidth(), getShape()->getDepth(), CREATURE_SIGHT_RADIUS ) : session->getClosestGoodGuy ( toint ( getX() ), toint ( getY() ), getShape()->getWidth(), getShape()->getDepth(), CREATURE_SIGHT_RADIUS ) );
2219   } else {
2220     p = ( possessed ? session->getClosestGoodGuy ( toint ( getX() ), toint ( getY() ), getShape()->getWidth(), getShape()->getDepth(), CREATURE_SIGHT_RADIUS ) : session->getClosestMonster ( toint ( getX() ), toint ( getY() ), getShape()->getWidth(), getShape()->getDepth(), CREATURE_SIGHT_RADIUS ) );
2221   }
2222 
2223   return p;
2224 }
2225 
2226 /// Returns a random suitable battle target, NULL if no target found.
2227 
getRandomTarget()2228 Creature *Creature::getRandomTarget() {
2229   Creature *p;
2230   bool possessed = getStateMod( StateMod::possessed );
2231 
2232   // Select a suitable target.
2233   if ( isMonster() ) {
2234     p = ( possessed ? session->getRandomNearbyMonster ( toint ( getX() ), toint ( getY() ), getShape()->getWidth(), getShape()->getDepth(), CREATURE_SIGHT_RADIUS ) : session->getRandomNearbyGoodGuy ( toint ( getX() ), toint ( getY() ), getShape()->getWidth(), getShape()->getDepth(), CREATURE_SIGHT_RADIUS ) );
2235   } else {
2236     p = ( possessed ? session->getRandomNearbyGoodGuy ( toint ( getX() ), toint ( getY() ), getShape()->getWidth(), getShape()->getDepth(), CREATURE_SIGHT_RADIUS ) : session->getRandomNearbyMonster ( toint ( getX() ), toint ( getY() ), getShape()->getWidth(), getShape()->getDepth(), CREATURE_SIGHT_RADIUS ) );
2237   }
2238 
2239   return p;
2240 }
2241 
2242 /// Makes the creature attack the closest suitable target. Returns true if target found.
2243 
attackClosestTarget()2244 bool Creature::attackClosestTarget() {
2245 
2246   Creature *p = getClosestTarget();
2247 
2248   if ( p ) {
2249     // attack with item
2250     setTargetCreature ( p );
2251   }
2252 
2253   return ( p != NULL );
2254 }
2255 
2256 /// Makes the creature attack a random suitable target. Returns true if target found.
2257 
attackRandomTarget()2258 bool Creature::attackRandomTarget() {
2259   Creature *p = getRandomTarget();
2260 
2261   if ( p ) {
2262     // attack with item
2263     setTargetCreature ( p );
2264   }
2265 
2266   return ( p != NULL );
2267 }
2268 
2269 /// Try to heal someone; returns true if someone was found.
2270 
castHealingSpell(bool checkOnly)2271 bool Creature::castHealingSpell( bool checkOnly ) {
2272   vector<Spell*> healingSpells;
2273   Spell *spell;
2274 
2275   for ( int i = 0; i < getSpellCount(); i++ ) {
2276     spell = getSpell ( i );
2277     if ( spell->isFriendly() && ( spell->getMp() < getMp() ) && spell->hasStateModPrereq() && ( findClosestTargetWithPrereq( spell ) != NULL ) && ( spell->isStateModPrereqAPotionSkill() ? ( spell->getStateModPrereq() != Constants::AC ) : true ) ) {
2278       healingSpells.push_back ( spell );
2279     }
2280   }
2281 
2282   // We can't cast a healing spell, exit
2283   if ( healingSpells.empty() ) return false;
2284 
2285   if ( !checkOnly ) {
2286     spell = healingSpells[ Util::pickOne ( 0, healingSpells.size() - 1 ) ];
2287     setAction ( Constants::ACTION_CAST_SPELL, NULL, spell );
2288     setTargetCreature ( findClosestTargetWithPrereq( spell ) );
2289     setMotion( Constants::MOTION_MOVE_TOWARDS );
2290   }
2291 
2292   return true;
2293 }
2294 
2295 /// Tries to cast an offensive spell onto the targetted or nearest suitable creature.
2296 
castOffensiveSpell(bool checkOnly)2297 bool Creature::castOffensiveSpell( bool checkOnly ) {
2298   vector<Spell*> offensiveSpells;
2299   Spell *spell;
2300 
2301   // Gather spells that are suitable for auto-casting.
2302 
2303   for ( int i = 0; i < getSpellCount(); i++ ) {
2304     spell = getSpell ( i );
2305     if ( !spell->isFriendly() && ( spell->getMp() < getMp() ) && spell->isCreatureTargetAllowed() && ( spell->hasStateModPrereq() ? findClosestTargetWithPrereq( spell ) != NULL : getClosestTarget() != NULL ) ) {
2306       offensiveSpells.push_back ( spell );
2307     }
2308   }
2309 
2310   // We can't cast an offensive spell, exit
2311   if ( offensiveSpells.empty() ) return false;
2312 
2313   if ( !checkOnly ) {
2314     spell = offensiveSpells[ Util::pickOne ( 0, offensiveSpells.size() - 1 ) ];
2315     setAction ( Constants::ACTION_CAST_SPELL, NULL, spell );
2316     if ( spell->hasStateModPrereq() ) setTargetCreature( findClosestTargetWithPrereq( spell ) ); else setTargetCreature( getClosestTarget() );
2317     setMotion( Constants::MOTION_MOVE_TOWARDS );
2318   }
2319 
2320   return true;
2321 }
2322 
2323 /// Tries to cast an area-affecting spell near the targetted or nearest suitable creature.
2324 
castAreaSpell(bool checkOnly)2325 bool Creature::castAreaSpell( bool checkOnly ) {
2326   vector<Spell*> areaSpells;
2327   Spell *spell;
2328 
2329   // Gather spells that are suitable for auto-casting.
2330 
2331   for ( int i = 0; i < getSpellCount(); i++ ) {
2332     spell = getSpell ( i );
2333     if ( !spell->isFriendly() && ( spell->getMp() < getMp() ) && spell->isLocationTargetAllowed() && ( spell->hasStateModPrereq() ? findClosestTargetWithPrereq( spell ) != NULL : getClosestTarget() != NULL ) ) {
2334       areaSpells.push_back ( spell );
2335     }
2336   }
2337 
2338   // We can't cast an offensive spell, exit
2339   if ( areaSpells.empty() ) return false;
2340 
2341   if ( !checkOnly ) {
2342     spell = areaSpells[ Util::pickOne ( 0, areaSpells.size() - 1 ) ];
2343     setAction ( Constants::ACTION_CAST_SPELL, NULL, spell );
2344     if ( spell->hasStateModPrereq() ) setTargetCreature( findClosestTargetWithPrereq( spell ) ); else setTargetCreature( getClosestTarget() );
2345     setMotion( Constants::MOTION_MOVE_TOWARDS );
2346   }
2347 
2348   return true;
2349 }
2350 
2351 /// Try to raise someone's AC using a spell; returns true if someone was found.
2352 
castACSpell(bool checkOnly)2353 bool Creature::castACSpell( bool checkOnly ) {
2354   vector<Spell*> acSpells;
2355   Spell *spell;
2356 
2357   for ( int i = 0; i < getSpellCount(); i++ ) {
2358     spell = getSpell ( i );
2359     if ( spell->isFriendly() && ( spell->getMp() < getMp() ) && spell->hasStateModPrereq() && ( findClosestTargetWithPrereq( spell ) != NULL ) && ( spell->isStateModPrereqAPotionSkill() ? ( spell->getStateModPrereq() == Constants::AC ) : false ) ) {
2360       acSpells.push_back ( spell );
2361     }
2362   }
2363 
2364   // We can't cast an AC raising spell, exit
2365   if ( acSpells.empty() ) return false;
2366 
2367   if ( !checkOnly ) {
2368     spell = acSpells[ Util::pickOne ( 0, acSpells.size() - 1 ) ];
2369     setAction ( Constants::ACTION_CAST_SPELL, NULL, spell );
2370     setTargetCreature ( findClosestTargetWithPrereq( spell ) );
2371     setMotion( Constants::MOTION_MOVE_TOWARDS );
2372   }
2373 
2374   return true;
2375 }
2376 
2377 /// Tries to use a healing magical item from the backpack on oneself.
2378 
useMagicItem(bool checkOnly)2379 bool Creature::useMagicItem( bool checkOnly ) {
2380 
2381   // If there are no magical items in the backpack, get outta here.
2382   if ( !backpack->getContainedItemCount() || !backpack->getContainsMagicItem() ) return false;
2383 
2384   vector<Item*> usefulItems;
2385   Item *item;
2386 
2387   // Search the backpack for potions and items with healing spells.
2388   for ( int i = 0; i < backpack->getContainedItemCount(); i++ ) {
2389     item = backpack->getContainedItem( i );
2390     if ( isWithPrereq( item ) ) {
2391       usefulItems.push_back( item );
2392     }
2393   }
2394 
2395   // If no items found, leave.
2396   if ( usefulItems.empty() ) return false;
2397 
2398   if ( !checkOnly ) {
2399     item = usefulItems [ Util::pickOne( 0, usefulItems.size() - 1 ) ];
2400     setAction( ( ( item->getRpgItem()->getPotionSkill() < 0 ) ? Constants::ACTION_EAT_DRINK : Constants::ACTION_CAST_SPELL ), item, item->getSpell() );
2401   }
2402 
2403   return true;
2404 }
2405 
2406 /// Returns the distance to the selected target spot.
2407 
getDistanceToSel()2408 float Creature::getDistanceToSel() {
2409 	if ( selX > -1 && selY > -1 ) {
2410 		return Constants::distance( getX(),  getY(), getShape()->getWidth(), getShape()->getDepth(), selX, selY, 1, 1 );
2411 	}
2412 
2413 return 0;
2414 }
2415 
2416 /// Returns the distance to another creature.
2417 
getDistance(RenderedCreature * other)2418 float Creature::getDistance( RenderedCreature *other ) {
2419 	return Constants::distance( getX(),  getY(),
2420 	                            getShape()->getWidth(), getShape()->getDepth(),
2421 	                            other->getX(),
2422 	                            other->getY(),
2423 	                            other->getShape()->getWidth(),
2424 	                            other->getShape()->getDepth() );
2425 }
2426 
2427 /// Returns the distance to a creature or if not given, the selected target.
2428 
getDistanceToTarget(RenderedCreature * creature)2429 float Creature::getDistanceToTarget( RenderedCreature *creature ) {
2430 	if ( creature ) return getDistance( creature );
2431 
2432 	if ( !hasTarget() ) return 0.0f;
2433 	if ( getTargetCreature() ) {
2434 		return getDistance( getTargetCreature() );
2435 	} else if ( getTargetX() || getTargetY() ) {
2436 		if ( getTargetItem() ) {
2437 			return Constants::distance( getX(),  getY(),
2438 			                            getShape()->getWidth(), getShape()->getDepth(),
2439 			                            getTargetX(), getTargetY(),
2440 			                            getTargetItem()->getShape()->getWidth(),
2441 			                            getTargetItem()->getShape()->getDepth() );
2442 		} else {
2443 			return Constants::distance( getX(),  getY(),
2444 			                            getShape()->getWidth(), getShape()->getDepth(),
2445 			                            getTargetX(), getTargetY(), 1, 1 );
2446 		}
2447 	} else {
2448 		return 0.0f;
2449 	}
2450 }
2451 
2452 /// Sets the experience required for the character to level up.
2453 
setExp()2454 void Creature::setExp() {
2455 	if ( !( isPartyMember() || isWanderingHero() ) ) return;
2456 	expOfNextLevel = 0;
2457 	for ( int i = 0; i < level - 1; i++ ) {
2458 		expOfNextLevel += ( ( i + 1 ) * character->getLevelProgression() );
2459 	}
2460 }
2461 
2462 /// Calculates how far the creature has moved since the last frame.
2463 
getStep()2464 GLfloat Creature::getStep() {
2465 	GLfloat fps = session->getGameAdapter()->getFps();
2466 	GLfloat div = FPS_ONE + static_cast<float>( ( 4 - session->getPreferences()->getGameSpeedLevel() ) * 3.0f );
2467 	if ( fps < div ) return 0.8f;
2468 	GLfloat step = 1.0f / ( fps / div  );
2469 	if ( pathManager->getSpeed() <= 0 ) {
2470 		step *= 1.0f - ( 1.0f / 10.0f );
2471 	} else if ( pathManager->getSpeed() >= 10 ) {
2472 		step *= 1.0f - ( 9.9f / 10.0f );
2473 	} else {
2474 		step *= ( 1.0f - ( ( GLfloat )( pathManager->getSpeed() ) / 10.0f ) );
2475 	}
2476 	return step;
2477 }
2478 
2479 /// Returns a brief description of the creature.
2480 
getDetailedDescription(std::string & s)2481 void Creature::getDetailedDescription( std::string& s ) {
2482 	char tempdesc[256] = {0};
2483 
2484 	int alignmentPercent = (int)( getAlignment() * 100 );
2485 	char alignmentDesc[32];
2486 
2487 	if ( alignmentPercent < 20 ) {
2488 		snprintf( alignmentDesc, 32, _( "Utterly chaotic" ) );
2489 	} else if ( alignmentPercent >= 20 && alignmentPercent < 40 ) {
2490 		snprintf( alignmentDesc, 32, _( "Chaotic" ) );
2491 	} else if ( alignmentPercent >= 40 && alignmentPercent < 60 ) {
2492 		snprintf( alignmentDesc, 32, _( "Neutral" ) );
2493 	} else if ( alignmentPercent >= 60 && alignmentPercent < 80 ) {
2494 		snprintf( alignmentDesc, 32, _( "Lawful" ) );
2495 	} else if ( alignmentPercent >= 80 ) {
2496 		snprintf( alignmentDesc, 32, _( "Overly lawful" ) );
2497 	}
2498 
2499 	snprintf( tempdesc, 255, _( "%s (L:%d HP:%d/%d MP:%d/%d AL:%d%%(%s))" ), _( getName() ), getLevel(), getHp(), getMaxHp(), getMp(), getMaxMp(), alignmentPercent, alignmentDesc );
2500 
2501 	s = tempdesc;
2502 
2503 	if ( session->getCurrentMission() && session->getCurrentMission()->isMissionCreature( this ) ) {
2504 		s += _( " *Mission*" );
2505 	}
2506 	if ( boss ) {
2507 		s += _( " *Boss*" );
2508 	}
2509 
2510 }
2511 
2512 /// Draws the creature.
2513 
draw()2514 void Creature::draw() {
2515 	getShape()->draw();
2516 }
2517 
2518 /// Adds additional NPC-only info.
2519 
setNpcInfo(NpcInfo * npcInfo)2520 void Creature::setNpcInfo( NpcInfo *npcInfo ) {
2521 	this->npcInfo = npcInfo;
2522 	setName( npcInfo->name );
2523 
2524 	// for merchants, re-create backpack with the correct types
2525 	if ( npcInfo->type == Constants::NPC_TYPE_MERCHANT ) {
2526 		// drop everything beyond the basic backpack
2527 		for ( int i = getMonster()->getStartingItemCount(); i < this->getBackpackContentsCount(); i++ ) {
2528 			this->removeFromBackpack( getMonster()->getStartingItemCount() );
2529 		}
2530 
2531 		std::vector<int> types( npcInfo->getSubtype()->size() + 1 );
2532 		int typeCount = 0;
2533 		for ( set<int>::iterator e = npcInfo->getSubtype()->begin(); e != npcInfo->getSubtype()->end(); ++e ) {
2534 			types[ typeCount++ ] = *e;
2535 		}
2536 
2537 		// equip merchants at the party's level
2538 		int level = ( session->getParty() ?
2539 		              toint( ( static_cast<float>( session->getParty()->getTotalLevel() ) ) / ( static_cast<float>( session->getParty()->getPartySize() ) ) ) :
2540 		              getLevel() );
2541 		if ( level < 0 ) level = 1;
2542 
2543 		// add some loot
2544 		int nn = Util::pickOne( 3, 7 );
2545 		//cerr << "Adding loot:" << nn << endl;
2546 		for ( int i = 0; i < nn; i++ ) {
2547 			Item *loot;
2548 			if ( npcInfo->isSubtype( RpgItem::SCROLL ) ) {
2549 				Spell *spell = MagicSchool::getRandomSpell( level );
2550 				loot = session->newItem( RpgItem::getItemByName( "Scroll" ),
2551 				                         level,
2552 				                         spell );
2553 			} else {
2554 				loot =
2555 				  session->newItem(
2556 				    RpgItem::getRandomItemFromTypes( session->getGameAdapter()->
2557 				        getCurrentDepth(),
2558 				        &types[0], typeCount ),
2559 				    level );
2560 			}
2561 			//cerr << "\t" << loot->getRpgItem()->getName() << endl;
2562 			// make it contain all items, no matter what size
2563 			addToBackpack( loot );
2564 		}
2565 	}
2566 }
2567 
2568 /// Sets up the special capabilities of the creature.
2569 
evalSpecialSkills()2570 void Creature::evalSpecialSkills() {
2571 
2572 	if( !( isPartyMember() || isWanderingHero() ) || !SQUIRREL_ENABLED ) return;
2573 
2574 	set<SpecialSkill*> oldSpecialSkills;
2575 	for ( int t = 0; t < SpecialSkill::getSpecialSkillCount(); t++ ) {
2576 		SpecialSkill *ss = SpecialSkill::getSpecialSkill( t );
2577 		if ( specialSkills.find( ss ) != specialSkills.end() ) {
2578 			oldSpecialSkills.insert( ss );
2579 		}
2580 	}
2581 	enum {TMP_SIZE = 120};
2582 	char tmp[TMP_SIZE];
2583 	specialSkills.clear();
2584 	specialSkillNames.clear();
2585 	HSQOBJECT *param = session->getSquirrel()->getCreatureRef( this );
2586 	if ( param ) {
2587 		bool result;
2588 		for ( int t = 0; t < SpecialSkill::getSpecialSkillCount(); t++ ) {
2589 			SpecialSkill *ss = SpecialSkill::getSpecialSkill( t );
2590 			session->getSquirrel()->
2591 			callBoolMethod( ss->getSquirrelFunctionPrereq(),
2592 			                param,
2593 			                &result );
2594 			if ( result ) {
2595 				specialSkills.insert( ss );
2596 				string skillName = ss->getName();
2597 				specialSkillNames.insert( skillName );
2598 				if ( oldSpecialSkills.find( ss ) == oldSpecialSkills.end() ) {
2599 					if ( session->getParty()->isPartyMember( this ) ) {
2600 						snprintf( tmp, TMP_SIZE, _( "%1$s gains the %2$s special ability!" ), getName(), ss->getDisplayName() );
2601 						session->getGameAdapter()->writeLogMessage( tmp, Constants::MSGTYPE_STATS );
2602 					}
2603 				}
2604 			}
2605 		}
2606 	}
2607 	for ( int t = 0; t < SpecialSkill::getSpecialSkillCount(); t++ ) {
2608 		SpecialSkill *ss = SpecialSkill::getSpecialSkill( t );
2609 		if ( specialSkills.find( ss ) == specialSkills.end() &&
2610 		        oldSpecialSkills.find( ss ) != oldSpecialSkills.end() ) {
2611 			if ( session->getParty()->isPartyMember( this ) ) {
2612 				snprintf( tmp, TMP_SIZE, _( "%1$s looses the %2$s special ability!" ), getName(), ss->getDisplayName() );
2613 				session->getGameAdapter()->writeLogMessage( tmp, Constants::MSGTYPE_STATS );
2614 			}
2615 		}
2616 	}
2617 }
2618 
2619 /// Sets the value of a skill.
2620 
setSkill(int index,int value)2621 void Creature::setSkill( int index, int value ) {
2622 	int oldValue = getSkill( index );
2623 	skills[index] = ( value < 0 ? 0 : value > 100 ? 100 : value );
2624 	skillChanged( index, oldValue, getSkill( index ) );
2625 	evalSpecialSkills();
2626 	session->getParty()->recomputeMaxSkills();
2627 }
2628 
2629 /// Sets the additional skill bonus (on top of the base value).
2630 
setSkillBonus(int index,int value)2631 void Creature::setSkillBonus( int index, int value ) {
2632 	int oldValue = getSkill( index );
2633 	skillBonus[index] = value;
2634 	skillChanged( index, oldValue, getSkill( index ) );
2635 	session->getParty()->recomputeMaxSkills();
2636 }
2637 
2638 /// Sets the not yet applied skill amount.
2639 
setSkillMod(int index,int value)2640 void Creature::setSkillMod( int index, int value ) {
2641 	int oldValue = getSkill( index );
2642 	skillMod[ index ] = ( value < 0 ? 0 : value );
2643 	skillChanged( index, oldValue, getSkill( index ) );
2644 	session->getParty()->recomputeMaxSkills();
2645 }
2646 
2647 /// Recalculates skills when stats change.
2648 
skillChanged(int index,int oldValue,int newValue)2649 void Creature::skillChanged( int index, int oldValue, int newValue ) {
2650 	// while loading don't update skill values.
2651 	if ( loading ) return;
2652 
2653 	if ( Skill::skills[ index ]->getGroup()->isStat() && character ) {
2654 		for ( int i = 0; i < Skill::SKILL_COUNT; i++ ) {
2655 			int oldPrereq = 0;
2656 			int newPrereq = 0;
2657 			for ( int t = 0; t < Skill::skills[i]->getPreReqStatCount(); t++ ) {
2658 				int statIndex = Skill::skills[i]->getPreReqStat( t )->getIndex();
2659 				if ( statIndex == index ) {
2660 					oldPrereq += oldValue;
2661 					newPrereq += newValue;
2662 				} else {
2663 					oldPrereq += getSkill( statIndex );
2664 					newPrereq += getSkill( statIndex );
2665 				}
2666 			}
2667 			oldPrereq = static_cast<int>( ( oldPrereq / static_cast<float>( Skill::skills[i]->getPreReqStatCount() ) ) *
2668 			                              static_cast<float>( Skill::skills[i]->getPreReqMultiplier() ) );
2669 			newPrereq = static_cast<int>( ( newPrereq / static_cast<float>( Skill::skills[i]->getPreReqStatCount() ) ) *
2670 			                              static_cast<float>( Skill::skills[i]->getPreReqMultiplier() ) );
2671 			if ( oldPrereq != newPrereq ) {
2672 				setSkill( i, getSkill( i ) + ( newPrereq - oldPrereq ) );
2673 				armorChanged = true;
2674 			}
2675 		}
2676 	}
2677 }
2678 
2679 /// Applies the skill point based changes to skills.
2680 
applySkillMods()2681 void Creature::applySkillMods() {
2682 	for ( int i = 0; i < Skill::SKILL_COUNT; i++ ) {
2683 		if ( skillMod[ i ] > 0 ) {
2684 			setSkill( i, skills[ i ] + skillMod[ i ] );
2685 			skillMod[ i ] = 0;
2686 		}
2687 	}
2688 	availableSkillMod = 0;
2689 	hasAvailableSkillPoints = false;
2690 }
2691 
2692 /// Sets the active state of a state mod.
2693 
setStateMod(int mod,bool setting)2694 void Creature::setStateMod( int mod, bool setting ) {
2695 	if ( setting ) stateMod |= ( 1 << mod );
2696 	else stateMod &= ( ( GLuint )0xffff - ( GLuint )( 1 << mod ) );
2697 	evalSpecialSkills();
2698 }
2699 
2700 /// Sets the "protected" state of a state mod (not influenceable by spells etc.)
2701 
setProtectedStateMod(int mod,bool setting)2702 void Creature::setProtectedStateMod( int mod, bool setting ) {
2703 	if ( setting ) protStateMod |= ( 1 << mod );
2704 	else protStateMod &= ( ( GLuint )0xffff - ( GLuint )( 1 << mod ) );
2705 	evalSpecialSkills();
2706 }
2707 
2708 /// Applies the effects of recurring (periodic) special capabilities.
2709 
applyRecurringSpecialSkills()2710 void Creature::applyRecurringSpecialSkills() {
2711 	for ( int t = 0; t < SpecialSkill::getSpecialSkillCount(); t++ ) {
2712 		SpecialSkill *skill = SpecialSkill::getSpecialSkill( t );
2713 		if ( skill->getType() == SpecialSkill::SKILL_TYPE_RECURRING &&
2714 		        hasSpecialSkill( skill ) ) {
2715 			useSpecialSkill( skill, false );
2716 		}
2717 	}
2718 }
2719 
2720 /// Applies the effects of automatic special capabilities.
applyAutomaticSpecialSkills(int event,char * varName,float varValue)2721 float Creature::applyAutomaticSpecialSkills( int event, char *varName, float varValue ) {
2722 	if( !( isPartyMember() || isWanderingHero() ) || !SQUIRREL_ENABLED ) return varValue;
2723 
2724 #ifdef DEBUG_CAPABILITIES
2725 	cerr << "Using automatic capabilities for event type: " << event << endl;
2726 #endif
2727 	for ( int t = 0; t < SpecialSkill::getSpecialSkillCount(); t++ ) {
2728 		SpecialSkill *skill = SpecialSkill::getSpecialSkill( t );
2729 		if ( skill->getEvent() == event &&
2730 		        skill->getType() == SpecialSkill::SKILL_TYPE_AUTOMATIC &&
2731 		        hasSpecialSkill( skill ) ) {
2732 #ifdef DEBUG_CAPABILITIES
2733 			cerr << "\tusing capability: " << skill->getDisplayName() <<
2734 			" and setting var: " <<
2735 			varName << "=" << varValue << endl;
2736 #endif
2737 			session->getSquirrel()->setGlobalVariable( varName, varValue );
2738 			useSpecialSkill( skill, false );
2739 			varValue = session->getSquirrel()->getGlobalVariable( varName );
2740 #ifdef DEBUG_CAPABILITIES
2741 			cerr << "\t\tgot back " << varValue << endl;
2742 #endif
2743 		}
2744 	}
2745 #ifdef DEBUG_CAPABILITIES
2746 	cerr << "final value=" << varValue << " ===============================" << endl;
2747 #endif
2748 	return varValue;
2749 }
2750 
2751 /// Uses a special capability.
2752 
useSpecialSkill(SpecialSkill * specialSkill,bool manualOnly)2753 char *Creature::useSpecialSkill( SpecialSkill *specialSkill, bool manualOnly ) {
2754 	if( monster || !SQUIRREL_ENABLED ) return NULL;
2755 
2756 	if ( !hasSpecialSkill( specialSkill ) ) {
2757 		return Constants::getMessage( Constants::UNMET_CAPABILITY_PREREQ_ERROR );
2758 	} else if ( manualOnly &&
2759 	            specialSkill->getType() !=
2760 	            SpecialSkill::SKILL_TYPE_MANUAL ) {
2761 		return Constants::getMessage( Constants::CANNOT_USE_AUTO_CAPABILITY_ERROR );
2762 	}
2763 	HSQOBJECT *param = session->getSquirrel()->getCreatureRef( this );
2764 	if ( param ) {
2765 		session->getSquirrel()->
2766 		callNoArgMethod( specialSkill->getSquirrelFunctionAction(),
2767 		                 param );
2768 		return NULL;
2769 	} else {
2770 		cerr << "*** Error: can't find squarrel reference for creature: " << getName() << endl;
2771 		return NULL;
2772 	}
2773 }
2774 
2775 
2776 
2777 
2778 
2779 
2780 /**
2781  * ============================================================
2782  * ============================================================
2783  *
2784  * New battle system calls
2785  *
2786  * damage=attack_roll - ac
2787  * attack_roll=(item_action + item_level) % skill
2788  * ac=(armor_total + avg_armor_level) % skill
2789  * skill=avg. of ability skill (power,coord, etc.), item skill, luck
2790  *
2791  * ============================================================
2792  * ============================================================
2793  *
2794  * Fixme:
2795  * -currently, extra attacks are not used. (use SPEED skill to eval?)
2796  * -magic item armor
2797  * -automatic capabilities
2798  *
2799  * move here from battle.cpp:
2800  * -critical hits (2x,3x,damage,etc.)
2801  * -conditions modifiers
2802  *
2803  * ** Look in old methods and battle.cpp for more info
2804  */
2805 
2806 // 1 item level equals this many character levels in combat
2807 #define ITEM_LEVEL_DIVISOR 8.0f
2808 
2809 // base weapon damage of an attack with bare hands
2810 #define HAND_ATTACK_DAMAGE Dice(1,4,0)
2811 
2812 /// Returns the creature's chance to dogde the specified attack.
2813 
getDodge(Creature * attacker,Item * weapon)2814 float Creature::getDodge( Creature *attacker, Item *weapon ) {
2815 	// the target's dodge if affected by angle of attack
2816 	bool inFOV =
2817 	  Util::isInFOV( getX(), getY(), getTargetAngle(),
2818 	                 attacker->getX(), attacker->getY() );
2819 
2820 	// the target's dodge
2821 	float armor, dodgePenalty;
2822 	getArmor( &armor, &dodgePenalty,
2823 	          weapon ? weapon->getRpgItem()->getDamageType() : 0,
2824 	          weapon );
2825 	float dodge = getSkill( Skill::DODGE_ATTACK ) - dodgePenalty;
2826 	if ( !inFOV ) {
2827 		dodge /= 2.0f;
2828 		if ( getCharacter() ) {
2829 			session->getGameAdapter()->writeLogMessage( _( "...Attack from blind-spot!" ), Constants::MSGTYPE_PLAYERBATTLE );
2830 		} else {
2831 			session->getGameAdapter()->writeLogMessage( _( "...Attack from blind-spot!" ), Constants::MSGTYPE_NPCBATTLE );
2832 		}
2833 	}
2834 	return dodge;
2835 }
2836 
2837 /// Returns the chance the creature's armor deflects the damage from the specified attack.
2838 
getArmor(float * armorP,float * dodgePenaltyP,int damageType,Item * vsWeapon)2839 float Creature::getArmor( float *armorP, float *dodgePenaltyP,
2840                           int damageType, Item *vsWeapon ) {
2841 	calcArmor( damageType, &armor, dodgePenaltyP, ( vsWeapon ? true : false ) );
2842 
2843 	// negative feedback: for monsters only, allow hits now and then
2844 	// -=K=-: the sentence in if seems screwed up
2845 	if ( monster &&
2846 	        ( Util::mt_rand() <
2847 	          monsterToughness[ session->getPreferences()->getMonsterToughness() ].
2848 	          armorMisfuction ) ) {
2849 		// 3.0f * rand() / RAND_MAX < 1.0f ) {
2850 		armor = Util::roll( 0.0f, armor / 2.0f );
2851 	} else if( !monster && SQUIRREL_ENABLED ) {
2852 		// apply any armor enhancing capabilities
2853 		if ( vsWeapon ) {
2854 			session->getSquirrel()->setCurrentWeapon( vsWeapon );
2855 			armor = applyAutomaticSpecialSkills( SpecialSkill::SKILL_EVENT_ARMOR,
2856 			        "armor", armor );
2857 		}
2858 	}
2859 
2860 	*armorP = armor;
2861 
2862 	return armor;
2863 }
2864 
2865 /// Calculates armor and dodge penalty.
2866 
calcArmor(int damageType,float * armorP,float * dodgePenaltyP,bool callScript)2867 void Creature::calcArmor( int damageType,
2868                           float *armorP,
2869                           float *dodgePenaltyP,
2870                           bool callScript ) {
2871 	if ( armorChanged ) {
2872 		for ( int t = 0; t < RpgItem::DAMAGE_TYPE_COUNT; t++ ) {
2873 			lastArmor[ t ] = ( monster ? monster->getBaseArmor() : 0 );
2874 			lastDodgePenalty[ t ] = 0;
2875 			int armorCount = 0;
2876 			for(int i = 0; i < Constants::EQUIP_LOCATION_COUNT; i++) {
2877 				if ( equipped[i] != MAX_BACKPACK_SIZE ) {
2878 					Item *item = backpack->getContainedItem( equipped[ i ] );
2879 					if ( item->getRpgItem()->getType() == RpgItem::ARMOR ||
2880 					        ( item->isMagicItem() && item->getBonus() > 0 && !item->getRpgItem()->isWeapon() ) ) {
2881 
2882 						int n = ( item->getRpgItem()->getType() == RpgItem::ARMOR ?
2883 						          item->getRpgItem()->getDefense( t ) :
2884 						          item->getBonus() );
2885 
2886 						if ( callScript && !monster && SQUIRREL_ENABLED ) {
2887 							session->getSquirrel()->setGlobalVariable( "armor", lastArmor[ t ] );
2888 							session->getSquirrel()->callItemEvent( this, item, "useItemInDefense" );
2889 							lastArmor[ t ] = session->getSquirrel()->getGlobalVariable( "armor" );
2890 						}
2891 						lastArmor[ t ] += n;
2892 						lastDodgePenalty[ t ] += item->getRpgItem()->getDodgePenalty();
2893 
2894 						// item's level has a small influence.
2895 						lastArmor[ t ] += item->getLevel() / 8;
2896 
2897 						// apply the armor influence... it uses the first
2898 						// influence slot (AP_INFLUENCE)
2899 						lastArmor[ t ] +=
2900 						  getInfluenceBonus( item, AP_INFLUENCE,
2901 						                     ( callScript ? "CTH" : NULL ) );
2902 
2903 						armorCount++;
2904 					}
2905 				}
2906 			}
2907 			lastArmor[ t ] += bonusArmor;
2908 			if ( lastArmor[ t ] < 0 ) lastArmor[ t ] = 0;
2909 		}
2910 		armorChanged = false;
2911 	}
2912 
2913 	*armorP = lastArmor[ damageType ];
2914 	*dodgePenaltyP = lastDodgePenalty[ damageType ];
2915 }
2916 
2917 #define MAX_RANDOM_DAMAGE 2.0f
2918 
power(float base,int e)2919 float power( float base, int e ) {
2920 	float n = 1;
2921 	for ( int i = 0; i < e; i++ ) {
2922 		n *= base;
2923 	}
2924 	return n;
2925 }
2926 
getInfluenceBonus(Item * weapon,int influenceType,char const * debugMessage)2927 float Creature::getInfluenceBonus( Item *weapon,
2928     int influenceType,
2929     char const* debugMessage ) {
2930 
2931 	if ( !weapon ) return 0;
2932 
2933 	float bonus = 0;
2934 	for ( int i = 0; i < Skill::SKILL_COUNT; i++ ) {
2935 		WeaponInfluence *minInfluence = weapon->getRpgItem()->getWeaponInfluence( i, influenceType, MIN_INFLUENCE );
2936 		WeaponInfluence *maxInfluence = weapon->getRpgItem()->getWeaponInfluence( i, influenceType, MAX_INFLUENCE );
2937 
2938 		float n = 0;
2939 		int value = getSkill( i );
2940 		if ( minInfluence->limit > -1 && minInfluence->limit > value ) {
2941 			switch ( minInfluence->type ) {
2942 			case 'E' :
2943 				// exponential malus
2944 				n = -power( minInfluence->base, minInfluence->limit - value );
2945 				break;
2946 			case 'L' :
2947 				// linear
2948 				n = -( minInfluence->limit - value ) * minInfluence->base;
2949 				break;
2950 			default:
2951 				cerr << "*** Error: unknown influence type for item: " << weapon->getRpgItem()->getDisplayName() << endl;
2952 			}
2953 		} else if ( maxInfluence->limit > -1 && maxInfluence->limit < value ) {
2954 			switch ( maxInfluence->type ) {
2955 			case 'E' :
2956 				// exponential bonus
2957 				n = power( maxInfluence->base, value - maxInfluence->limit );
2958 				break;
2959 			case 'L' :
2960 				// linear bonus
2961 				n = ( value - maxInfluence->limit ) * maxInfluence->base;
2962 				break;
2963 			default:
2964 				cerr << "*** Error: unknown influence type for item: " << weapon->getRpgItem()->getDisplayName() << endl;
2965 			}
2966 		}
2967 
2968 		if ( n != 0 && debugMessage &&
2969 		        session->getPreferences()->getCombatInfoDetail() > 0 ) {
2970 			char message[120];
2971 			snprintf( message, 120, "...%s %s:%s %d-%d %s %d, %s=%.2f",
2972 			          debugMessage,
2973 			          _( "skill" ),
2974 			          Skill::skills[i]->getDisplayName(),
2975 			          minInfluence->limit,
2976 			          maxInfluence->limit,
2977 			          _( "vs." ),
2978 			          value,
2979 			          _( "bonus" ),
2980 			          n );
2981 			session->getGameAdapter()->writeLogMessage( message, Constants::MSGTYPE_SYSTEM );
2982 		}
2983 
2984 		bonus += n;
2985 	}
2986 	return bonus;
2987 }
2988 
2989 /// Returns the chance to hit with a weapon, as well as the skill used for the weapon.
2990 
getCth(Item * weapon,float * cth,float * skill,bool showDebug)2991 void Creature::getCth( Item *weapon, float *cth, float *skill, bool showDebug ) {
2992 	// the attacker's skill
2993 	*skill = getSkill( weapon ?
2994 	                   weapon->getRpgItem()->getDamageSkill() :
2995 	                   Skill::HAND_TO_HAND_COMBAT );
2996 
2997 	// The max cth is closer to the skill to avoid a lot of misses
2998 	// This is ok, since dodge is subtracted from it anyway.
2999 	float maxCth = *skill * 1.5f;
3000 	if ( maxCth > 100 ) maxCth = 100;
3001 	if ( maxCth < 40 ) maxCth = 40;
3002 
3003 	// roll chance to hit (CTH)
3004 	*cth = Util::roll( 0.0f, maxCth );
3005 
3006 	// item's level has a small influence
3007 	if ( weapon ) *skill += weapon->getLevel() / 2;
3008 
3009 	// Apply COORDINATION influence to skill
3010 	// (As opposed to subtracting it from cth. This is b/c
3011 	// skill is shown in the characterInfo as the cth.)
3012 	*skill += getInfluenceBonus( weapon, CTH_INFLUENCE,
3013 	                             ( showDebug ? "CTH" : NULL ) );
3014 	if ( *skill < 0 ) *skill = 0;
3015 
3016 	if ( showDebug && session->getPreferences()->getCombatInfoDetail() > 0 ) {
3017 		char message[120];
3018 		snprintf( message, 120, "...%s:%.2f (%s:%.2f) %s %s:%.2f",
3019 		          _( "CTH" ),
3020 		          *cth,
3021 		          _( "max" ),
3022 		          maxCth,
3023 		          _( "vs." ),
3024 		          _( "skill" ),
3025 		          *skill );
3026 		session->getGameAdapter()->writeLogMessage( message, Constants::MSGTYPE_SYSTEM );
3027 	}
3028 }
3029 
3030 /// Returns the chance to successfully attack with a weapon, as well as the min and max damage.
3031 
getAttack(Item * weapon,float * maxP,float * minP,bool callScript)3032 float Creature::getAttack( Item *weapon,
3033                            float *maxP,
3034                            float *minP,
3035                            bool callScript ) {
3036 
3037 	float power;
3038 	if ( weapon && weapon->getRpgItem()->isRangedWeapon() ) {
3039 		power = getSkill( Skill::POWER ) / 2.0f +
3040 		        getSkill( Skill::COORDINATION ) / 2.0f;
3041 	} else {
3042 		power = getSkill( Skill::POWER );
3043 	}
3044 
3045 	// the min/max power value
3046 	float minPower, maxPower;
3047 	if ( power < 10 ) {
3048 		// 1d10
3049 		minPower = 1; maxPower = 10;
3050 	} else if ( power < 15 ) {
3051 		// 2d10
3052 		minPower = 2; maxPower = 20;
3053 	} else {
3054 		// 3d10
3055 		minPower = 3; maxPower = 30;
3056 	}
3057 
3058 	// What percent of power is given by weapon?
3059 	// (For unarmed combat it's a coordination bonus.)
3060 	float damagePercent = ( weapon ?
3061 	                        weapon->getRpgItem()->getDamage() :
3062 	                        getSkill( Skill::COORDINATION ) +
3063 	                        getSkill( Skill::POWER ) );
3064 
3065 	// item's level has a small influence
3066 	if ( weapon ) damagePercent += weapon->getLevel() / 2;
3067 
3068 	// apply POWER influence
3069 	damagePercent += getInfluenceBonus( weapon, DAM_INFLUENCE,
3070 	                 ( callScript ? "DAM" : NULL ) );
3071 	if ( damagePercent < 0 ) damagePercent = 0;
3072 
3073 	if ( minP ) {
3074 		*minP = ( minPower / 100.0f ) * damagePercent;
3075 	}
3076 	if ( maxP ) {
3077 		*maxP = ( maxPower / 100.0f ) * damagePercent;
3078 	}
3079 
3080 	// roll the power
3081 	float roll = Util::roll( minPower, maxPower );
3082 
3083 	// take the weapon's skill % of the max power
3084 	roll = ( roll / 100.0f ) * damagePercent;
3085 
3086 	// apply damage enhancing capabilities
3087 	if ( callScript && isPartyMember() && SQUIRREL_ENABLED ) {
3088 		session->getSquirrel()->setCurrentWeapon( weapon );
3089 		roll = applyAutomaticSpecialSkills( SpecialSkill::SKILL_EVENT_DAMAGE,
3090 		       "damage", roll );
3091 		if ( weapon )
3092 			session->getSquirrel()->setGlobalVariable( "damage", roll );
3093 		session->getSquirrel()->callItemEvent( this, weapon, "useItemInAttack" );
3094 		roll = session->getSquirrel()->getGlobalVariable( "damage" );
3095 	}
3096 
3097 	return roll;
3098 }
3099 
3100 /// The chance to parry a successful attack before it hits armor.
3101 
getParry(Item ** parryItem)3102 float Creature::getParry( Item **parryItem ) {
3103 	int location[] = {
3104 		Constants::EQUIP_LOCATION_RIGHT_HAND,
3105 		Constants::EQUIP_LOCATION_LEFT_HAND,
3106 		Constants::EQUIP_LOCATION_WEAPON_RANGED,
3107 		-1
3108 	};
3109 	float ret = 0;
3110 	float maxParry = 0;
3111 	for ( int i = 0; location[i] > -1; i++ ) {
3112 		Item *item = getEquippedItem( location[i] );
3113 		if ( !item ) continue;
3114 		if ( item->getRpgItem()->getDefenseSkill() == Skill::SHIELD_DEFEND ) {
3115 			// parry using a shield: use shield skill to parry
3116 			maxParry = getSkill( item->getRpgItem()->getDefenseSkill() );
3117 		} else if ( item->getRpgItem()->getParry() > 0 ) {
3118 			// parry using a weapon: get max parry skill amount (% of weapon skill)
3119 			maxParry =
3120 			  ( getSkill( item->getRpgItem()->getDamageSkill() ) / 100.0f ) *
3121 			  item->getRpgItem()->getParry();
3122 
3123 			// use the item's CTH skill to modify parry also
3124 			maxParry += getInfluenceBonus( item, CTH_INFLUENCE, "PARRY" );
3125 			if ( maxParry < 0 ) maxParry = 0;
3126 
3127 		} else {
3128 			// no parry with this hand
3129 			continue;
3130 		}
3131 		// roll to parry
3132 		float parry = Util::roll( 0.0f, maxParry );
3133 		// select the highest value
3134 		if ( ret == 0 || ret < parry ) {
3135 			ret = parry;
3136 			*parryItem = item;
3137 		}
3138 	}
3139 	return ret;
3140 }
3141 
3142 /// Modifies the attack roll according to the attacked creature's active state mods.
3143 
getDefenderStateModPercent(bool magical)3144 float Creature::getDefenderStateModPercent( bool magical ) {
3145 	/*
3146 	  apply state_mods:
3147 	  (Done here so it's used for spells too)
3148 
3149 	  blessed,
3150 	  empowered,
3151 	  enraged,
3152 	  ac_protected,
3153 	  magic_protected,
3154 
3155 	  drunk,
3156 
3157 	  poisoned,
3158 	  cursed,
3159 	  possessed,
3160 	  blinded,
3161 	  charmed,
3162 	  changed,
3163 	  overloaded,
3164 	*/
3165 	float delta = 0.0f;
3166 	if ( getStateMod( StateMod::blessed ) ) {
3167 		delta += Util::roll( 0.0f, 10.0f );
3168 	}
3169 	if ( getStateMod( StateMod::empowered ) ) {
3170 		delta += Util::roll( 5.0f, 15.0f );
3171 	}
3172 	if ( getStateMod( StateMod::enraged ) ) {
3173 		delta += Util::roll( 8.0f, 18.0f );
3174 	}
3175 	if ( getStateMod( StateMod::drunk ) ) {
3176 		delta += Util::roll( -7.0f, 7.0f );
3177 	}
3178 	if ( getStateMod( StateMod::cursed ) ) {
3179 		delta -= Util::roll( 5.0f, 15.0f );
3180 	}
3181 	if ( getStateMod( StateMod::blinded ) ) {
3182 		delta -= Util::roll( 0.0f, 10.0f );
3183 	}
3184 	if ( !magical && getTargetCreature()->getStateMod( StateMod::ac_protected ) ) {
3185 		delta -= Util::roll( 0.0f, 7.0f );
3186 	}
3187 	if ( magical && getTargetCreature()->getStateMod( StateMod::magic_protected ) ) {
3188 		delta -= ( 7.0f * Util::mt_rand() );
3189 	}
3190 	if ( getTargetCreature()->getStateMod( StateMod::blessed ) ) {
3191 		delta -= Util::roll( 0.0f, 5.0f );
3192 	}
3193 	if ( getTargetCreature()->getStateMod( StateMod::cursed ) ) {
3194 		delta += Util::roll( 0.0f, 5.0f );
3195 	}
3196 	if ( getTargetCreature()->getStateMod( StateMod::overloaded ) ) {
3197 		delta += Util::roll( 0.0f, 2.0f );
3198 	}
3199 	if ( getTargetCreature()->getStateMod( StateMod::blinded ) ) {
3200 		delta += Util::roll( 0.0f, 2.0f );
3201 	}
3202 	if ( getTargetCreature()->getStateMod( StateMod::invisible ) ) {
3203 		delta -= Util::roll( 0.0f, 10.0f );
3204 	}
3205 	return delta;
3206 }
3207 
3208 /// Modifies the attack roll according to the creature's active state mods.
3209 
getAttackerStateModPercent()3210 float Creature::getAttackerStateModPercent() {
3211 	/*
3212 	  apply state_mods:
3213 	  blessed,
3214 	 empowered,
3215 	 enraged,
3216 	 ac_protected,
3217 	 magic_protected,
3218 
3219 	 drunk,
3220 
3221 	 poisoned,
3222 	 cursed,
3223 	 possessed,
3224 	 blinded,
3225 	 charmed,
3226 	 changed,
3227 	 overloaded,
3228 	*/
3229 	float delta = 0.0f;
3230 	if ( getStateMod( StateMod::blessed ) ) {
3231 		delta += Util::roll( 0.0f, 15.0f );
3232 	}
3233 	if ( getStateMod( StateMod::empowered ) ) {
3234 		delta += Util::roll( 10.0f, 25.0f );
3235 	}
3236 	if ( getStateMod( StateMod::enraged ) ) {
3237 		delta -= Util::roll( 0.0f, 10.0f );
3238 	}
3239 	if ( getStateMod( StateMod::drunk ) ) {
3240 		delta += Util::roll( -15.0f, 15.0f );
3241 	}
3242 	if ( getStateMod( StateMod::cursed ) ) {
3243 		delta -= Util::roll( 10.0f, 25.0f );
3244 	}
3245 	if ( getStateMod( StateMod::blinded ) ) {
3246 		delta -= Util::roll( 0.0f, 15.0f );
3247 	}
3248 	if ( getStateMod( StateMod::overloaded ) ) {
3249 		delta -= Util::roll( 0.0f, 10.0f );
3250 	}
3251 	if ( getStateMod( StateMod::invisible ) ) {
3252 		delta += Util::roll( 5.0f, 10.0f );
3253 	}
3254 	return delta;
3255 }
3256 
3257 /// Returns a semi-random amount of magical damage for an item.
3258 
rollMagicDamagePercent(Item * item)3259 float Creature::rollMagicDamagePercent( Item *item ) {
3260 	float itemLevel = ( item->getLevel() - 1 ) / ITEM_LEVEL_DIVISOR;
3261 	return item->rollMagicDamage() + itemLevel;
3262 }
3263 
3264 /// Returns the number of action points the creature receives at each battle turn.
3265 
getMaxAP()3266 float Creature::getMaxAP( ) {
3267 	return( static_cast<float>( getSkill( Skill::COORDINATION ) ) + static_cast<float>( getSkill( Skill::SPEED ) ) ) / 2.0f;
3268 }
3269 
3270 /// Returns the attacks per round possible with item.
3271 
getAttacksPerRound(Item * item)3272 float Creature::getAttacksPerRound( Item *item ) {
3273 	return( getMaxAP() / getWeaponAPCost( item, false ) );
3274 }
3275 
3276 /// Returns the action point cost of using a specific item.
3277 
getWeaponAPCost(Item * item,bool showDebug)3278 float Creature::getWeaponAPCost( Item *item, bool showDebug ) {
3279 	float baseAP = ( item ?
3280 	                 item->getRpgItem()->getAP() :
3281 	                 Constants::HAND_WEAPON_SPEED );
3282 	// never show debug (called a lot)
3283 	// Apply a max 3pt influence to weapon AP
3284 	baseAP -= getInfluenceBonus( item, AP_INFLUENCE, NULL );
3285 	// can't be free..
3286 	if ( baseAP < 1 ) baseAP = 1;
3287 	return baseAP;
3288 }
3289 
3290 /// Checks whether an item can be equipped, returns error message or NULL if successful.
3291 
canEquipItem(Item * item,bool interactive)3292 char *Creature::canEquipItem( Item *item, bool interactive ) {
3293 
3294 	// check item tags to see if this item can be equipped.
3295 	if ( character ) {
3296 		if ( !character->canEquip( item->getRpgItem() ) ) {
3297 			return Constants::getMessage( Constants::ITEM_ACL_VIOLATION );
3298 		}
3299 	}
3300 
3301 	// check the level
3302 	if ( getLevel() < item->getLevel() ) {
3303 		return Constants::getMessage( Constants::ITEM_LEVEL_VIOLATION );
3304 	}
3305 
3306 	// two handed weapon violations
3307   if( item->getRpgItem()->getEquip() & Constants::EQUIP_LOCATION_LEFT_HAND ||
3308       item->getRpgItem()->getEquip() & Constants::EQUIP_LOCATION_RIGHT_HAND ) {
3309     Item *leftHandWeapon = getEquippedItem( Constants::EQUIP_LOCATION_LEFT_HAND );
3310     Item *rightHandWeapon = getEquippedItem( Constants::EQUIP_LOCATION_RIGHT_HAND );
3311 		bool bothHandsFree = !( leftHandWeapon || rightHandWeapon );
3312 		bool holdsTwoHandedWeapon =
3313 		  ( ( leftHandWeapon && leftHandWeapon->getRpgItem()->getTwoHanded() == RpgItem::ONLY_TWO_HANDED ) ||
3314 		    ( rightHandWeapon && rightHandWeapon->getRpgItem()->getTwoHanded() == RpgItem::ONLY_TWO_HANDED ) );
3315 
3316 		if ( holdsTwoHandedWeapon ||
3317 		        ( !bothHandsFree &&
3318 		          item->getRpgItem()->getTwoHanded() == RpgItem::ONLY_TWO_HANDED ) ) {
3319 			if ( interactive ) {
3320 				return Constants::getMessage( Constants::ITEM_TWO_HANDED_VIOLATION );
3321 			}
3322 		}
3323 	}
3324 	return NULL;
3325 }
3326 
3327 /// Sets character info (if not monster/NPC).
3328 
setCharacter(Character * c)3329 void Creature::setCharacter( Character *c ) {
3330 	assert( isPartyMember() || isWanderingHero() );
3331 	character = c;
3332 }
3333 
3334 /// Plays a character sound of the specified type.
3335 
playCharacterSound(int soundType,int panning)3336 void Creature::playCharacterSound( int soundType, int panning ) {
3337 	if ( !monster )
3338 		session->getSound()->playCharacterSound( model_name.c_str(), soundType, panning );
3339 }
3340 
3341 /// Does a roll against a skill, optionally with a luck modifier.
3342 
rollSkill(int skill,float luckDiv)3343 bool Creature::rollSkill( int skill, float luckDiv ) {
3344 	float f = static_cast<float>( getSkill( skill ) );
3345 	if ( luckDiv > 0 )
3346 		f += static_cast<float>( getSkill( Skill::LUCK ) ) / luckDiv;
3347 	return( Util::roll( 0.0f, 100.0f ) <= f );
3348 }
3349 
3350 /// Does a secret door discovery roll, returns true if successful.
3351 
3352 #define SECRET_DOOR_ATTEMPT_INTERVAL 5000
rollSecretDoor(Location * pos)3353 bool Creature::rollSecretDoor( Location *pos ) {
3354 	if ( secretDoorAttempts.find( pos ) != secretDoorAttempts.end() ) {
3355 		Uint32 lastTime = secretDoorAttempts[ pos ];
3356 		if ( SDL_GetTicks() - lastTime < SECRET_DOOR_ATTEMPT_INTERVAL ) return false;
3357 	}
3358 	bool ret = rollSkill( Skill::FIND_SECRET_DOOR, 4.0f );
3359 	if ( !ret ) {
3360 		secretDoorAttempts[ pos ] = SDL_GetTicks();
3361 	}
3362 	return ret;
3363 }
3364 
resetSecretDoorAttempts()3365 void Creature::resetSecretDoorAttempts() {
3366 	secretDoorAttempts.clear();
3367 }
3368 
3369 /// Unused.
3370 
3371 #define TRAP_FIND_ATTEMPT_INTERVAL 500
rollTrapFind(Trap * trap)3372 bool Creature::rollTrapFind( Trap *trap ) {
3373 	if ( trapFindAttempts.find( trap ) != trapFindAttempts.end() ) {
3374 		Uint32 lastTime = trapFindAttempts[ trap ];
3375 		if ( SDL_GetTicks() - lastTime < TRAP_FIND_ATTEMPT_INTERVAL ) return false;
3376 	}
3377 	bool ret = rollSkill( Skill::FIND_TRAP, 0.5f ); // traps are easy to notice
3378 	if ( !ret ) {
3379 		trapFindAttempts[ trap ] = SDL_GetTicks();
3380 	}
3381 	return ret;
3382 }
3383 
resetTrapFindAttempts()3384 void Creature::resetTrapFindAttempts() {
3385 	trapFindAttempts.clear();
3386 }
3387 
3388 /// Does a perception roll, discovers secret doors and traps if successful.
3389 
rollPerception()3390 void Creature::rollPerception() {
3391 
3392 	Uint32 now = SDL_GetTicks();
3393 	if ( now - lastPerceptionCheck < PERCEPTION_DELTA ) return;
3394 	lastPerceptionCheck = now;
3395 
3396 	// find traps
3397 	set<Uint8> *trapsShown = session->getMap()->getTrapsShown();
3398 	for ( set<Uint8>::iterator e = trapsShown->begin(); e != trapsShown->end(); ++e ) {
3399 		Uint8 trapIndex = *e;
3400 		Trap *trap = session->getMap()->getTrapLoc( trapIndex );
3401 		if ( trap->discovered == false ) {
3402 			float dist = Constants::distance( getX(), getY(), getShape()->getWidth(), getShape()->getDepth(),
3403 			             trap->r.x, trap->r.y, trap->r.w, trap->r.h );
3404 			if ( dist < 10 && !session->getMap()->isWallBetween( toint( getX() ), toint( getY() ), 0, trap->r.x, trap->r.y, 0 ) ) {
3405 				trap->discovered = rollSkill( Skill::FIND_TRAP, 0.5f ); // traps are easy to notice
3406 				if ( trap->discovered ) {
3407 					char message[ 120 ];
3408 					snprintf( message, 120, _( "%s notices a trap!" ), getName() );
3409 					int panning = session->getMap()->getPanningFromMapXY( trap->r.x, trap->r.y );
3410 					session->getSound()->playSound( "notice-trap", panning );
3411 					session->getGameAdapter()->writeLogMessage( message, Constants::MSGTYPE_MISSION );
3412 					addExperienceWithMessage( 50 );
3413 					setMotion( Constants::MOTION_STAND );
3414 					stopMoving();
3415 				}
3416 			}
3417 		}
3418 	}
3419 
3420 	// find secret doors
3421 	for ( int xx = toint( getX() ) - 10; xx < toint( getX() ) + 10; xx++ ) {
3422 		for ( int yy = toint( getY() ) - 10; yy < toint( getY() ) + 10; yy++ ) {
3423 			Location *pos = session->getMap()->getLocation( xx, yy, 0 );
3424 			if ( pos && session->getMap()->isSecretDoor( pos ) && !session->getMap()->isSecretDoorDetected( pos ) ) {
3425 				if ( rollSkill( Skill::FIND_SECRET_DOOR, 4.0f ) ) {
3426 					session->getMap()->setSecretDoorDetected( pos );
3427 					char message[ 120 ];
3428 					snprintf( message, 120, _( "%s notices a secret door!" ), getName() );
3429 					int panning = session->getMap()->getPanningFromMapXY( pos->x, pos->y );
3430 					session->getSound()->playSound( "notice-trap", panning );
3431 					session->getGameAdapter()->writeLogMessage( message, Constants::MSGTYPE_MISSION );
3432 					addExperienceWithMessage( 50 );
3433 				}
3434 			}
3435 		}
3436 	}
3437 }
3438 
3439 /// Checks whether the creature has stepped into a trap and handles the effects.
3440 
evalTrap()3441 void Creature::evalTrap() {
3442 	int trapIndex = session->getMap()->getTrapAtLoc( toint( getX() ), toint( getY() ) );
3443 	if ( trapIndex != -1 ) {
3444 		Trap *trap = session->getMap()->getTrapLoc( trapIndex );
3445 		if ( trap->enabled ) {
3446 			trap->discovered = true;
3447 			trap->enabled = false;
3448 			int damage = static_cast<int>( Util::getRandomSum( 10, session->getCurrentMission()->getLevel() ) );
3449 			char message[ 120 ];
3450 			snprintf( message, 120, _( "%1$s blunders into a trap and takes %2$d points of damage!" ), getName(), damage );
3451 			int panning = session->getMap()->getPanningFromMapXY( trap->r.x, trap->r.y );
3452 			session->getSound()->playSound( "trigger-trap", panning );
3453 			if ( getCharacter() ) {
3454 				session->getGameAdapter()->writeLogMessage( message, Constants::MSGTYPE_PLAYERDAMAGE );
3455 			} else {
3456 				session->getGameAdapter()->writeLogMessage( message, Constants::MSGTYPE_NPCDAMAGE );
3457 			}
3458 			takeDamage( damage );
3459 		}
3460 	}
3461 }
3462 
3463 /// Handles trap disarming.
3464 
disableTrap(Trap * trap)3465 void Creature::disableTrap( Trap *trap ) {
3466 	if ( trap->enabled ) {
3467 		trap->discovered = true;
3468 		trap->enabled = false;
3469 		enum { MSG_SIZE = 120 };
3470 		char message[ MSG_SIZE ];
3471 		snprintf( message, MSG_SIZE, _( "%s attempts to disable the trap:" ), getName() );
3472 		session->getGameAdapter()->writeLogMessage( message );
3473 		bool ret = rollSkill( Skill::FIND_TRAP, 5.0f );
3474 		if ( ret ) {
3475 			session->getGameAdapter()->writeLogMessage( _( "   and succeeds!" ), Constants::MSGTYPE_MISSION );
3476 			int panning = session->getMap()->getPanningFromMapXY( trap->r.x, trap->r.y );
3477 			session->getSound()->playSound( "disarm-trap", panning );
3478 			int exp = static_cast<int>( Util::getRandomSum( 50, session->getCurrentMission()->getLevel() ) );
3479 			addExperienceWithMessage( exp );
3480 		} else {
3481 			int damage = static_cast<int>( Util::getRandomSum( 10, session->getCurrentMission()->getLevel() ) );
3482 			char message[ MSG_SIZE ];
3483 			snprintf( message, MSG_SIZE, _( "    and fails! %1$s takes %2$d points of damage!" ), getName(), damage );
3484 			int panning = session->getMap()->getPanningFromMapXY( trap->r.x, trap->r.y );
3485 			session->getSound()->playSound( "trigger-trap", panning );
3486 			session->getGameAdapter()->writeLogMessage( message, Constants::MSGTYPE_FAILURE );
3487 			takeDamage( damage );
3488 		}
3489 	} else {
3490 		session->getGameAdapter()->writeLogMessage( _( "This trap is already disabled." ) );
3491 	}
3492 }
3493 
3494 /// Sets the motion type (stand, run, loiter around...) for the creature.
3495 
setMotion(int motion)3496 void Creature::setMotion( int motion ) {
3497 	this->motion = motion;
3498 }
3499 
3500 /// Will this creature stay visible in movie mode?
3501 
setScripted(bool b)3502 void Creature::setScripted( bool b ) {
3503 	this->scripted = b;
3504 	if ( scripted ) stopMoving();
3505 }
3506 
3507 /// Draws the creature's portrait in movie mode.
3508 
drawMoviePortrait(int width,int height)3509 void Creature::drawMoviePortrait( int width, int height ) {
3510 	// todo: should be next power of 2 after width/height (maybe cap-ed at 256)
3511 	int textureSizeW = 128;
3512 	int textureSizeH = 128;
3513 
3514 	if ( !portrait.isSpecified() ) {
3515 		if ( getCharacter() != NULL ) {
3516 			getSession()->getShapePalette()->getPortraitTexture( getSex(), getPortraitTextureIndex() ).glBind();
3517 		} else {
3518 			getMonster()->getPortraitTexture().glBind();
3519 		}
3520 
3521 		portrait.createAlpha( session->getShapePalette()->getNamedTexture( "conv_filter" ),
3522 		              getCharacter() ? getSession()->getShapePalette()->getPortraitTexture( getSex(), getPortraitTextureIndex() ) :
3523 		              getMonster()->getPortraitTexture(), 128, 128, width, height );
3524 	}
3525 
3526 	glDisable( GL_DEPTH_TEST );
3527 	glDisable( GL_CULL_FACE );
3528 	glEnable( GL_TEXTURE_2D );
3529 	//    glTranslatef( x, y, 0 );
3530 
3531 	glEnable( GL_BLEND );
3532 // glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
3533 	glBlendFunc( Scourge::blendA, Scourge::blendB );
3534 	portrait.glBind();
3535 	glColor4f( 1, 1, 1, 1 );
3536 
3537 	glBegin( GL_TRIANGLE_STRIP );
3538 	glTexCoord2f( 0, 1 );
3539 	glVertex3f( 0, 0, 0 );
3540 	glTexCoord2f( 1, 1 );
3541 	glVertex3f( textureSizeW, 0, 0 );
3542 	glTexCoord2f( 0, 0 );
3543 	glVertex3f( 0, textureSizeH, 0 );
3544 	glTexCoord2f( 1, 0 );
3545 	glVertex3f( textureSizeW, textureSizeH, 0 );
3546 	glEnd();
3547 
3548 	session->getShapePalette()->getNamedTexture( "conv" ).glBind();
3549 	glColor4f( 1, 1, 1, 1 );
3550 
3551 	glPushMatrix();
3552 	glTranslatef( -10, -20, 0 );
3553 
3554 	glBegin( GL_TRIANGLE_STRIP );
3555 	glTexCoord2f( 0, 0 );
3556 	glVertex3f( 0, 0, 0 );
3557 	glTexCoord2f( 1, 0 );
3558 	glVertex3f( width + 20, 0, 0 );
3559 	glTexCoord2f( 0, 1 );
3560 	glVertex3f( 0, height + 40, 0 );
3561 	glTexCoord2f( 1, 1 );
3562 	glVertex3f( width + 20, height + 40, 0 );
3563 	glEnd();
3564 	glPopMatrix();
3565 
3566 	glDisable( GL_BLEND );
3567 	glEnable( GL_DEPTH_TEST );
3568 
3569 	//glDisable( GL_ALPHA_TEST );
3570 	//glDisable(GL_TEXTURE_2D);
3571 	//glEnable( GL_CULL_FACE );
3572 }
3573 
3574 /// Draws the creature's portrait, if it exists, else it draws a little 3D view of the creature.
3575 
drawPortrait(int width,int height,bool inFrame)3576 void Creature::drawPortrait( int width, int height, bool inFrame ) {
3577 	if ( getCharacter() || ( getMonster() && getMonster()->getPortraitTexture().isSpecified() ) ) {
3578 		//glEnable( GL_ALPHA_TEST );
3579 		//glAlphaFunc( GL_EQUAL, 0xff );
3580 		glEnable( GL_TEXTURE_2D );
3581 		glPushMatrix();
3582 		//    glTranslatef( x, y, 0 );
3583 		if (getCharacter() != NULL) {
3584 			getSession()->getShapePalette()->getPortraitTexture( getSex(), getPortraitTextureIndex() ).glBind();
3585 		} else {
3586 			getMonster()->getPortraitTexture().glBind();
3587 		}
3588 
3589 		glColor4f( 1, 1, 1, 1 );
3590 
3591 
3592 		glBegin( GL_TRIANGLE_STRIP );
3593 		glTexCoord2f( 0, 0 );
3594 		glVertex3f( 0, 0, 0 );
3595 		glTexCoord2f( 1, 0 );
3596 		glVertex3f( width, 0, 0 );
3597 		glTexCoord2f( 0, 1 );
3598 		glVertex3f( 0, height, 0 );
3599 		glTexCoord2f( 1, 1 );
3600 		glVertex3f( width, height, 0 );
3601 		glEnd();
3602 		glPopMatrix();
3603 
3604 		//glDisable( GL_ALPHA_TEST );
3605 		glDisable( GL_TEXTURE_2D );
3606 	} else if ( getMonster() ) {
3607 
3608 		glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT );
3609 		glEnable( GL_DEPTH_TEST );
3610 
3611 		Texture* textureGroup = session->getMap()->getShapes()->getCurrentTheme()->getTextureGroup( WallTheme::themeRefName[ WallTheme::THEME_REF_WALL ] );
3612 		Texture texture = textureGroup[ GLShape::FRONT_SIDE ];
3613 
3614 		glPushMatrix();
3615 		glEnable( GL_TEXTURE_2D );
3616 		glColor4f( 1, 1, 1, 1 );
3617 		texture.glBind();
3618 
3619 		glBegin( GL_TRIANGLE_STRIP );
3620 		glTexCoord2f( 0, 0 );
3621 		glVertex2i( 20, 0 );
3622 		glTexCoord2f( 1, 0 );
3623 		glVertex2i( 170, 0 );
3624 		glTexCoord2f( 0, 1 );
3625 		glVertex2i( 20, 150 );
3626 		glTexCoord2f( 1, 1 );
3627 		glVertex2i( 170, 150 );
3628 		glEnd();
3629 		glDisable( GL_TEXTURE_2D );
3630 		glPopMatrix();
3631 
3632 		shape = getShape();
3633 		shape->setCurrentAnimation( MD2_STAND, true );
3634 
3635 		// seems to have no effect...
3636 		((AnimatedShape*)shape)->setAlpha( 180.0f );
3637 
3638 		glPushMatrix();
3639 		glTranslatef( 135, 190, 100 );
3640 		glRotatef( 90, 1, 0, 0 );
3641 		glRotatef( 180, 0, 0, 1 );
3642 		glScalef( 2.0f, 2.0f, 2.0f );
3643 		glColor4f( 1, 1, 1, 1 );
3644 		shape->draw();
3645 		glPopMatrix();
3646 		glScalef( 1, 1, 1 );
3647 
3648 		glPopAttrib();
3649 	}
3650 }
3651 
3652 /// The item at a specified backpack index.
getBackpackItem(int backpackIndex)3653 Item *Creature::getBackpackItem( int backpackIndex ) {
3654 	return backpack->getContainedItem( backpackIndex );
3655 }
3656 /// Number of items carried in backpack.
getBackpackContentsCount()3657 int Creature::getBackpackContentsCount() {
3658 	return backpack->getContainedItemCount();
3659 }
3660 
3661 
3662 
3663 
3664 
3665 /// Returns the max hit points of the creature.
3666 
getMaxHp()3667 int Creature::getMaxHp() {
3668 	return getStartingHp() * getLevel();
3669 }
3670 
3671 /// Returns the max magic points of the creature.
3672 
getMaxMp()3673 int Creature::getMaxMp() {
3674 	return getStartingMp() * getLevel();
3675 }
3676 
3677 /// Sets the full amount of hit points.
3678 
setHp()3679 void Creature::setHp() {
3680 	int total = getLevel() * getStartingHp();
3681 	hp = Util::pickOne( (int)( total * 0.75f ), total );
3682 }
3683 
3684 /// Sets the full amount of magic points.
3685 
setMp()3686 void Creature::setMp() {
3687 	int total = getLevel() * getStartingMp();
3688 	mp = Util::pickOne( (int)( total * 0.75f ), total );
3689 }
3690 
getStartingHp()3691 int Creature::getStartingHp() {
3692 	return getCharacter() ? getCharacter()->getStartingHp() : startingHp;
3693 }
3694 
getStartingMp()3695 int Creature::getStartingMp() {
3696 	return getCharacter() ? getCharacter()->getStartingMp() : startingMp;
3697 }
3698 
monsterInit()3699 void Creature::monsterInit() {
3700 
3701 	setLevel( monster->getLevel() );
3702 
3703 
3704 	//cerr << "------------------------------------" << endl << "Creature: " << monster->getType() << endl;
3705 	for ( int i = 0; i < Skill::SKILL_COUNT; i++ ) {
3706 
3707 		//int n = Creature::rollStartingSkill( scourge->getSession(), LEVEL, i );
3708 		int n;
3709 		if ( Skill::skills[i]->getGroup()->isStat() ) {
3710 			MonsterToughness *mt = &( monsterToughness[ session->getPreferences()->getMonsterToughness() ] );
3711 			n = static_cast<int>( 20.0f * Util::roll( mt->minSkillBase, mt->maxSkillBase ) );
3712 		} else {
3713 			// create the starting value as a function of the stats
3714 			n = 0;
3715 			for ( int t = 0; t < Skill::skills[i]->getPreReqStatCount(); t++ ) {
3716 				int index = Skill::skills[i]->getPreReqStat( t )->getIndex();
3717 				n += getSkill( index );
3718 			}
3719 			n = static_cast<int>( ( n / static_cast<float>( Skill::skills[i]->getPreReqStatCount() ) ) * static_cast<float>( Skill::skills[i]->getPreReqMultiplier() ) );
3720 		}
3721 
3722 		// special: adjust magic resistance... makes game too hard otherwise
3723 		if ( i == Skill::RESIST_AWARENESS_MAGIC ||
3724 		        i == Skill::RESIST_CONFRONTATION_MAGIC ||
3725 		        i == Skill::RESIST_DECEIT_MAGIC ||
3726 		        i == Skill::RESIST_HISTORY_MAGIC ||
3727 		        i == Skill::RESIST_LIFE_AND_DEATH_MAGIC ||
3728 		        i == Skill::RESIST_NATURE_MAGIC ) {
3729 			n /= 2;
3730 		}
3731 
3732 		// apply monster settings
3733 		int minSkill = monster->getSkillLevel( Skill::skills[i]->getName() );
3734 		if ( minSkill > n ) n = minSkill;
3735 
3736 		setSkill( i, n );
3737 		//cerr << "\t" << Skill::skills[ i ]->getName() << "=" << getSkill( i ) << endl;
3738 
3739 		stateMod = monster->getStartingStateMod();
3740 	}
3741 
3742 	// equip starting backpack
3743 	for ( int i = 0; i < getMonster()->getStartingItemCount(); i++ ) {
3744 		int itemLevel = getMonster()->getLevel() - Util::dice(  2 );
3745 		if ( itemLevel < 1 ) itemLevel = 1;
3746 		Item *item = session->newItem( getMonster()->getStartingItem( i ), itemLevel );
3747 		if( addToBackpack( item ) ) {
3748 			equipFromBackpack( backpack->getContainedItemCount() - 1 );
3749 		}
3750 	}
3751 
3752 	// add some loot
3753 	int nn = Util::pickOne( 3, 7 );
3754 	//cerr << "Adding loot:" << nn << endl;
3755 	for ( int i = 0; i < nn; i++ ) {
3756 		Item *loot;
3757 		if ( 0 == Util::dice(  10 ) ) {
3758 			Spell *spell = MagicSchool::getRandomSpell( getLevel() );
3759 			loot = session->newItem( RpgItem::getItemByName( "Scroll" ),
3760 			                         getLevel(),
3761 			                         spell );
3762 		} else {
3763 			loot = session->newItem( RpgItem::getRandomItem( session->getGameAdapter()->getCurrentDepth() ),
3764 			                         getLevel() );
3765 		}
3766 		//cerr << "\t" << loot->getRpgItem()->getName() << endl;
3767 		// make it contain all items, no matter what size
3768 		addToBackpack( loot );
3769 	}
3770 
3771 	// add spells
3772 	for ( int i = 0; i < getMonster()->getStartingSpellCount(); i++ ) {
3773 		addSpell( getMonster()->getStartingSpell( i ) );
3774 	}
3775 
3776 	// add some hp and mp
3777 	if( monster->isNpc() ) {
3778 		// npc-s are initialized to be similar to pc-s
3779 		startingHp = monster->getHp();
3780 		startingMp = monster->getMp();
3781 		setHp();
3782 		setMp();
3783 	} else {
3784 		// monsters initialization
3785 		MonsterToughness mt = monsterToughness[ session->getPreferences()->getMonsterToughness() ];
3786 
3787 		startingHp = monster->getHp();
3788 		float n = static_cast<float>( startingHp * level );
3789 		hp = static_cast<int>( n * Util::roll( mt.minHpMpBase, mt.maxHpMpBase ) );
3790 
3791 		startingMp = monster->getMp();
3792 		n = static_cast<float>( startingMp * level );
3793 		mp = static_cast<int>( n * Util::roll( mt.minHpMpBase, mt.maxHpMpBase ) );
3794 	}
3795 }
3796 
summonCreature(bool friendly)3797 Creature *Creature::summonCreature( bool friendly ) {
3798 	Creature *creature = NULL;
3799 	if( (int)(getSummoned()->size()) >= getMaxSummonedCreatures() ) {
3800 		session->getGameAdapter()->writeLogMessage( _( "You may not summon any more creatures." ), Constants::MSGTYPE_STATS );
3801 	} else {
3802 		Monster *monster = Monster::getRandomMonster( Util::pickOne( (int)( getLevel() * 0.5f ), (int)( getLevel() * 0.75f ) ) );
3803 		if( monster ) {
3804 			cerr << "*** summoning monster: " << monster->getType() << endl;
3805 			creature = doSummon( monster, toint( getX() ), toint( getY() ), 0, 0, friendly );
3806 		} else {
3807 			cerr << "*** no monster summoned." << endl;
3808 		}
3809 	}
3810 	return creature;
3811 }
3812 
doSummon(Monster * monster,int cx,int cy,int ex,int ey,bool friendly)3813 Creature *Creature::doSummon( Monster *monster, int cx, int cy, int ex, int ey, bool friendly ) {
3814 	GLShape *shape = session->getShapePalette()->
3815 	                 getCreatureShape( monster->getModelName(),
3816 	                                   monster->getSkinName(),
3817 	                                   monster->getScale(),
3818 	                                   monster );
3819 	Creature *creature = session->newCreature( monster, shape );
3820 
3821 	// try to place on map
3822 	bool placed;
3823 	if( ex <= 0 ) {
3824 		cerr << "*** summon via findPlace: pos=" << cx << "," << cy << endl;
3825 		int x, y;
3826 		placed = creature->findPlace( cx, cy, &x, &y );
3827 		if( placed ) {
3828 			cerr << "\t*** final pos=" << x << "," << y << endl;
3829 		}
3830 	} else {
3831 		cerr << "*** summon via findPlaceBounded: region: " << cx << "," << cy << "-" << ex << "," << ey << endl;
3832 		placed = creature->findPlaceBounded( cx, cy, ex, ey );
3833 	}
3834 	if( !placed ) {
3835 		cerr << "*** warning: unable to place summoned creature." << endl;
3836 		return NULL;
3837 	}
3838 
3839 	addSummoned( creature );
3840 	creature->setSummoner( this );
3841 
3842 	// monster summons friendly or pc summons friendly: possess it
3843 	if( isMonster() != friendly ) {
3844 			// maybe make a new state mod for 'summonned'? Maybe not... all the battle code to update... argh
3845 			creature->setStateMod( StateMod::possessed, true );
3846 	}
3847 
3848 	// register with squirrel
3849 	session->getSquirrel()->registerCreature( creature );
3850 	for ( int i = 0; i < creature->getBackpackContentsCount(); i++ ) {
3851 		session->getSquirrel()->registerItem( creature->getBackpackItem( i ) );
3852 	}
3853 
3854 	creature->cancelTarget();
3855 	creature->startEffect( Constants::EFFECT_TELEPORT, ( Constants::DAMAGE_DURATION * 4 ) );
3856 
3857 	char message[200];
3858 	snprintf( message, 200, _( "%s calls for help: a %s appears!" ), getName(), monster->getType() );
3859 	session->getGameAdapter()->writeLogMessage( message, Constants::MSGTYPE_PLAYERMAGIC );
3860 
3861 	return creature;
3862 }
3863 
dismissSummonedCreatures()3864 void Creature::dismissSummonedCreatures() {
3865 	while( summoned.size() > 0 ) {
3866 		Creature *c = summoned[0];
3867 		c->dismissSummonedCreature();
3868 	}
3869 }
3870 
dismissSummonedCreature()3871 void Creature::dismissSummonedCreature() {
3872 	if( isSummoned() ) {
3873 		// remove from summoner's list
3874 		for( vector<Creature*>::iterator e = getSummoner()->getSummoned()->begin(); e != getSummoner()->getSummoned()->end(); ++e ) {
3875 			Creature *c = *e;
3876 			if( c == this ) {
3877 				getSummoner()->getSummoned()->erase( e );
3878 				break;
3879 			}
3880 		}
3881 		setSummoner( NULL );
3882 
3883 		// remove from the map; the object will be cleaned up at the end of the mission
3884 		session->getMap()->removeCreature( toint( getX() ), toint( getY() ), toint( getZ() ) );
3885 		setStateMod( StateMod::dead, true );
3886 		// cancel target, otherwise segfaults on resurrection
3887 		cancelTarget();
3888 
3889 		session->getMap()->startEffect( toint( getX() ), toint( getY() ), toint( getZ() ),
3890 		                                Constants::EFFECT_DUST, ( Constants::DAMAGE_DURATION * 4 ) );
3891 
3892 		char message[200];
3893 		snprintf( message, 200, _( "%s turns to vapors and disappears!" ), getName() );
3894 		session->getGameAdapter()->writeLogMessage( message, Constants::MSGTYPE_PLAYERMAGIC );
3895 	}
3896 }
3897 
3898 /// Returns the alignment of the creature as a float between 0.0 (fully chaotic) and 1.0 (totally lawful).
3899 
getAlignment()3900 float Creature::getAlignment() {
3901   int totalSpellCount, chaoticSpellCount, lawfulSpellCount;
3902   int totalSkillPointCount, chaoticSkillPointCount, lawfulSkillPointCount;
3903   float chaoticness, lawfulness;
3904   Spell *spell;
3905   Skill *skill;
3906 
3907   totalSpellCount = getSpellCount();
3908 
3909   if ( totalSpellCount == 0 ) {
3910     // If the creature has no spells, determine alignment by the distribution of certain stats.
3911     totalSkillPointCount = chaoticSkillPointCount = lawfulSkillPointCount = 0;
3912     string skillName;
3913 
3914     for ( int i = 0; i < Skill::SKILL_COUNT; i++ ) {
3915       skill = Skill::skills[ i ];
3916       skillName = skill->getName();
3917 
3918       // Evaluate only basic stats and resistances.
3919       if ( skill->getGroup()->isStat() || ( skillName.compare( 0, 7, "RESIST_" ) == 0 ) ) {
3920         totalSkillPointCount += getSkill( i, false );
3921 
3922         if ( skill->getAlignment() == ALIGNMENT_CHAOTIC ) {
3923           chaoticSkillPointCount += getSkill( i, false );
3924         } else if ( skill->getAlignment() == ALIGNMENT_LAWFUL ) {
3925           lawfulSkillPointCount += getSkill( i, false );
3926         }
3927 
3928       }
3929 
3930     }
3931 
3932     chaoticness = (float)chaoticSkillPointCount / (float)totalSkillPointCount;
3933     lawfulness = (float)lawfulSkillPointCount / (float)totalSkillPointCount;
3934 
3935   } else {
3936     // If the creature has spells, determine alignment by their selection.
3937     chaoticSpellCount = lawfulSpellCount = 0;
3938 
3939     for ( int i = 0; i < totalSpellCount; i++ ) {
3940       spell = getSpell( i );
3941 
3942       if ( spell->getSchool()->getBaseAlignment() == ALIGNMENT_CHAOTIC ) {
3943         chaoticSpellCount++;
3944       } else if ( spell->getSchool()->getBaseAlignment() == ALIGNMENT_LAWFUL ) {
3945         lawfulSpellCount++;
3946       }
3947 
3948     }
3949 
3950     // We don't need a neutralness value because it is already implied by the math done here.
3951     chaoticness = (float)chaoticSpellCount / (float)totalSpellCount;
3952     lawfulness = (float)lawfulSpellCount / (float)totalSpellCount;
3953   }
3954 
3955   return ( ( -chaoticness + lawfulness ) + 1 ) / 2;
3956 }
3957