1 /***************************************************************************
2                item.cpp  -  Class representing a specific item
3                              -------------------
4     begin                : Sun Sep 28 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 "item.h"
20 #include "render/renderlib.h"
21 #include "rpg/rpglib.h"
22 #include "session.h"
23 #include "shapepalette.h"
24 #include "configlang.h"
25 #include "creature.h"
26 
27 using namespace std;
28 
29 // ###### MS Visual C++ specific ######
30 #if defined(_MSC_VER) && defined(_DEBUG)
31 # define new DEBUG_NEW
32 # undef THIS_FILE
33   static char THIS_FILE[] = __FILE__;
34 #endif
35 
36 
Item(Session * session,RpgItem * rpgItem,int level,bool loading)37 Item::Item( Session *session, RpgItem *rpgItem, int level, bool loading ) {
38 	this->session = session;
39 	this->rpgItem = rpgItem;
40 	this->level = level;
41 	this->shapeIndex = ( rpgItem ? this->rpgItem->getShapeIndex() : 0 );
42 	this->color = NULL;
43 	this->shape = session->getShapePalette()->getShape( shapeIndex );
44 	// for now objects larger than 1 height will block (we can change this later)
45 	// this is so the player is not blocked by swords and axes on the ground
46 	this->blocking = ( shape->getHeight() > 1 ||
47 	                   ( rpgItem->getType() == RpgItem::CONTAINER &&
48 	                     strcasecmp( rpgItem->getName(), "corpse" ) ) ||
49 	                   rpgItem->getType() == RpgItem::MISSION );
50 	this->containedItemCount = 0;
51 	this->spell = NULL;
52 	this->containsMagicItem = false;
53 	this->showCursed = false;
54 	snprintf( this->itemName, ITEM_NAME_SIZE, "%s", rpgItem->getDisplayName() );
55 	backpackX = backpackY = 0;
56 	inventoryOf = NULL;
57 
58 	commonInit( loading );
59 
60 	currentCharges = rpgItem->getMaxCharges();
61 	weight = rpgItem->getWeight();
62 }
63 
~Item()64 Item::~Item() {
65 	/* unused:
66 	if ( textureInMemory != NULL ) {
67 	 free( textureInMemory );
68 	 textureInMemory = NULL;
69 	 glDeleteTextures( 1, tex3d );
70 	}
71 	*/
72 
73 	for ( int i = 0; i < PARTICLE_COUNT; i++ ) {
74 		delete iconUnderEffectParticle[i];
75 		iconUnderEffectParticle[i] = NULL;
76 		delete iconEffectParticle[i];
77 		iconEffectParticle[i] = NULL;
78 	}
79 }
80 
save()81 ItemInfo *Item::save() {
82 	ItemInfo *info = new ItemInfo;
83 	info->version = PERSIST_VERSION;
84 	info->level = getLevel();
85 	strcpy( ( char* )info->rpgItem_name, getRpgItem()->getName() );
86 	strcpy( ( char* )info->shape_name, getShape()->getName() );
87 	info->blocking = blocking;
88 	info->currentCharges = currentCharges;
89 	info->weight = ( Uint32 )( weight * 100 );
90 	info->quality = quality;
91 	info->price = price;
92 	info->identifiedBits = identifiedBits;
93 
94 	// spells
95 	strcpy( ( char* )info->spell_name, ( spell ? spell->getName() : "" ) );
96 
97 	// container
98 	int realCount = 0;
99 	for ( int i = 0; i < containedItemCount; i++ ) {
100 		if ( containedItems[i] ) info->containedItems[realCount++] = containedItems[i]->save();
101 	}
102 	info->containedItemCount = realCount;
103 
104 	// magic item
105 	info->bonus = bonus;
106 	info->damageMultiplier = damageMultiplier;
107 	info->cursed = cursed;
108 	info->magicLevel = magicLevel;
109 	strcpy( ( char* )info->monster_type, ( this->monsterType ? monsterType : "" ) );
110 	strcpy( ( char* )info->magic_school_name, ( this->school ? school->getName() : "" ) );
111 	info->magicDamage = ( school ? saveDice( magicDamage ) : saveEmptyDice() );
112 	for ( int i = 0; i < StateMod::STATE_MOD_COUNT; i++ ) {
113 		info->stateMod[i] = this->stateMod[i];
114 	}
115 	for ( int i = 0; i < Skill::SKILL_COUNT; i++ ) {
116 		info->skillBonus[i] = this->getSkillBonus( i );
117 	}
118 
119 	info->missionId = getMissionId();
120 	info->missionObjectiveIndex = getMissionObjectiveIndex();
121 	return info;
122 }
123 
saveDice(Dice * dice)124 DiceInfo *Item::saveDice( Dice *dice ) {
125 	DiceInfo *info = new DiceInfo;
126 	info->version = PERSIST_VERSION;
127 	info->count = dice->getCount();
128 	info->sides = dice->getSides();
129 	info->mod = dice->getMod();
130 	return info;
131 }
132 
saveEmptyDice()133 DiceInfo *Item::saveEmptyDice() {
134 	DiceInfo *info = new DiceInfo;
135 	info->version = PERSIST_VERSION;
136 	info->count = 0;
137 	info->sides = 0;
138 	info->mod = 0;
139 	return info;
140 }
141 
142 /// The item's damage dice (number of dices, sides per dice, modifier; example: 2d6+2 )
143 
loadDice(Session * session,DiceInfo * info)144 Dice *Item::loadDice( Session *session, DiceInfo *info ) {
145 	if ( !info->count ) return NULL;
146 	return new Dice( info->count, info->sides, info->mod );
147 }
148 
149 
load(Session * session,ItemInfo * info)150 Item *Item::load( Session *session, ItemInfo *info ) {
151 	if ( !strlen( ( char* )info->rpgItem_name ) ) return NULL;
152 	Spell *spell = NULL;
153 	if ( strlen( ( char* )info->spell_name ) ) spell = Spell::getSpellByName( ( char* )info->spell_name );
154 	RpgItem *rpgItem = RpgItem::getItemByName( ( char* )info->rpgItem_name );
155 	if ( !rpgItem ) {
156 		cerr << "Error: can't find rpgItem with name:" << ( char* )info->rpgItem_name << endl;
157 		return NULL;
158 	}
159 	Item *item = session->newItem( rpgItem,
160 	                               info->level,
161 	                               spell,
162 	                               true );
163 	item->blocking = ( info->blocking == 1 );
164 	item->currentCharges = info->currentCharges;
165 	item->weight = static_cast<float>( info->weight ) / 100.0f;
166 	item->quality = info->quality;
167 	item->price = info->price;
168 	item->identifiedBits = info->identifiedBits;
169 
170 	// container
171 	item->containedItemCount = 0;
172 	for ( int i = 0; i < static_cast<int>( info->containedItemCount ); i++ ) {
173 		Item *containedItem = Item::load( session, info->containedItems[i] );
174 		if ( containedItem ) {
175 			item->addContainedItem( containedItem );
176 		}
177 	}
178 
179 	// magic item
180 	item->bonus = info->bonus;
181 	item->damageMultiplier = info->damageMultiplier;
182 	item->cursed = ( info->cursed == 1 );
183 	item->magicLevel = info->magicLevel;
184 	// get a reference to the real string... (yuck)
185 	item->monsterType = Monster::getMonsterType( ( char* )info->monster_type );
186 	// turn off "vs. any creature"
187 	if ( !item->getMonsterType() ) item->damageMultiplier = 1;
188 	item->school = MagicSchool::getMagicSchoolByName( ( char* )info->magic_school_name );
189 	item->magicDamage = Item::loadDice( session, info->magicDamage );
190 	for ( int i = 0; i < StateMod::STATE_MOD_COUNT; i++ ) {
191 		item->stateMod[i] = info->stateMod[i];
192 	}
193 	for ( int i = 0; i < Skill::SKILL_COUNT; i++ ) {
194 		if ( info->skillBonus[i] ) item->skillBonus[i] = info->skillBonus[i];
195 	}
196 
197 	item->setMissionObjectInfo( static_cast<int>( info->missionId ), static_cast<int>( info->missionObjectiveIndex ) );
198 
199 	// re-describe the item. describeMagic is called from commonInit at
200 	// which point magicLevel can be 0, so it's important to re-describe
201 	// the item. (since later magicLevel can be -1)
202 	item->describeMagic( item->rpgItem->getDisplayName() );
203 
204 	return item;
205 }
206 
207 /// Puts another item inside this item.
208 
addContainedItem(Item * item,int itemX,int itemY)209 bool Item::addContainedItem( Item *item, int itemX, int itemY ) {
210 	if ( containedItemCount < MAX_CONTAINED_ITEMS && findInventoryPosition( item, itemX, itemY, true ) ) {
211 		containedItems[containedItemCount++] = item;
212 		if ( item->isMagicItem() ) containsMagicItem = true;
213 		return true;
214 	} else {
215 		cerr << "Warning: unable to add to container. Container=" << getRpgItem()->getName() << " item=" << item->getRpgItem()->getName() << endl;
216 		cerr << "\tcontainer: " << getName() << endl;
217 		cerr << "\tcontainedItemCount " << containedItemCount << " < " << MAX_CONTAINED_ITEMS << endl;
218 //		cerr << "\tforce: " << force << endl;
219 		return false;
220 	}
221 }
222 
223 /// Find an inventory position for an item
224 
225 /// note: optimize this,
226 /// current O(n^2)
227 
findInventoryPosition(Item * item,int posX,int posY,bool useExistingLocationForSameItem)228 bool Item::findInventoryPosition( Item *item, int posX, int posY, bool useExistingLocationForSameItem ) {
229 	if ( item ) {
230 		int colCount = getRpgItem()->getContainerWidth();
231 		int rowCount = getRpgItem()->getContainerHeight();
232 
233 		int selX = -1;
234 		int selY = -1;
235 
236 		for ( int xx = 0; xx < colCount; xx++ ) {
237 			for ( int yy = 0; yy < rowCount; yy++ ) {
238 				if ( xx + item->getBackpackWidth() <= colCount &&
239 				        yy + item->getBackpackHeight() <= rowCount &&
240 				        checkInventoryLocation( item, useExistingLocationForSameItem, xx, yy ) ) {
241 					if ( posX == xx && posY == yy ) {
242 						selX = xx;
243 						selY = yy;
244 						break;
245 					} else if ( selX == -1 ) {
246 						selX = xx;
247 						selY = yy;
248 					}
249 				}
250 			}
251 		}
252 
253 		if ( selX > -1 ) {
254 			item->setBackpackLocation( selX, selY );
255 			return true;
256 		}
257 	}
258 	return false;
259 }
260 
261 /// Checks whether an item fits into the inventory at pos xx,yy.
262 
checkInventoryLocation(Item * item,bool useExistingLocationForSameItem,int xx,int yy)263 bool Item::checkInventoryLocation( Item *item, bool useExistingLocationForSameItem, int xx, int yy ) {
264 	SDL_Rect itemRect;
265 	itemRect.x = xx;
266 	itemRect.y = yy;
267 	itemRect.w = item->getBackpackWidth();
268 	itemRect.h = item->getBackpackHeight();
269 	for ( int t = 0; t < getContainedItemCount(); t++ ) {
270 		Item *i = getContainedItem( t );
271 		if( inventoryOf && inventoryOf->isEquipped( i ) ) {
272 			continue;
273 		}
274 		if ( i == item ) {
275 			if ( useExistingLocationForSameItem ) {
276 				return true;
277 			} else {
278 				continue;
279 			}
280 		}
281 
282 		SDL_Rect iRect;
283 		iRect.x = i->getBackpackX();
284 		iRect.y = i->getBackpackY();
285 		iRect.w = i->getBackpackWidth();
286 		iRect.h = i->getBackpackHeight();
287 
288 		if ( SDLHandler::intersects( &itemRect, &iRect ) ) return false;
289 	}
290 	return true;
291 }
292 
293 /// Removes a contained item by index.
294 
removeContainedItem(Item * item)295 void Item::removeContainedItem( Item *item ) {
296 	for( int t = 0; t < containedItemCount; t++ ) {
297 		if( item == containedItems[ t ] ) {
298 			containedItemCount--;
299 			for ( int i = t; i < containedItemCount; i++ ) {
300 				containedItems[i] = containedItems[i + 1];
301 			}
302 			containsMagicItem = false;
303 			for ( int i = 0; i < containedItemCount; i++ ) {
304 				if ( containedItems[i]->isMagicItem() ) containsMagicItem = true;
305 			}
306 			return;
307 		}
308 	}
309 }
310 
311 /// Returns a contained item by index.
312 
getContainedItem(int index)313 Item *Item::getContainedItem( int index ) {
314 	return( ( index >= 0 && index < containedItemCount ) ? containedItems[index] : NULL );
315 }
316 
setContainedItem(int index,Item * item)317 void Item::setContainedItem( int index, Item *item ) {
318 	if( index >= 0 && index < containedItemCount ) {
319 		containedItems[ index ] = item;
320 	}
321 }
322 
323 /// Returns whether the item is inside a container.
324 
isContainedItem(Item * item)325 bool Item::isContainedItem( Item *item ) {
326 	for ( int i = 0; i < containedItemCount; i++ ) {
327 		if ( containedItems[i] == item ||
328 		        ( containedItems[i]->getRpgItem()->getType() == RpgItem::CONTAINER &&
329 		          containedItems[i]->isContainedItem( item ) ) ) return true;
330 	}
331 	return false;
332 }
333 
334 /// Creates a brief one-line description of the item.
335 
getDetailedDescription(std::string & s,bool precise)336 void Item::getDetailedDescription( std::string& s, bool precise ) {
337 	RpgItem * rpgItem  = getRpgItem();
338 	int type = rpgItem->getType();
339 	char str[20];
340 	snprintf( str, 20, _( "(L:%d) " ), getLevel() );
341 	s = str;
342 	if ( isCursed() && getShowCursed() ) s += _( "*Cursed* " );
343 
344 	if ( type == RpgItem::SCROLL ) {
345 		s += itemName;
346 	} else {
347 		s += precise ? itemName : rpgItem->getShortDesc();
348 	}
349 
350 	if ( missionId > 0 ) s += _( " *Mission*" );
351 }
352 
trim(char * s)353 char *trim( char *s ) {
354 	for ( char *p = s; p; p++ ) {
355 		if ( !( *p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' ) ) {
356 			return p;
357 		}
358 	}
359 	return s;
360 }
361 
362 /// Decrements the number of charges/uses. Returns true if the item is used up.
363 
decrementCharges()364 bool Item::decrementCharges() {
365 	float f1;
366 	int oldCharges;
367 
368 	oldCharges = getCurrentCharges();
369 	if ( oldCharges <= 1 ) {
370 		// The object is totally consummed
371 		return true;
372 	}
373 	setCurrentCharges( oldCharges - 1 );
374 
375 	// Compute initial weight to be able to compute new weight
376 	// (without increasing error each time)
377 
378 	f1 = getWeight();
379 	f1 *= static_cast<float>( getRpgItem()->getMaxCharges() );
380 	f1 /= static_cast<float>( oldCharges );
381 	f1 *= ( ( static_cast<float>( oldCharges ) - 1.0f ) / static_cast<float>( getRpgItem()->getMaxCharges() ) );
382 	setWeight( f1 );
383 	return false;
384 }
385 
386 
387 
388 
commonInit(bool loading)389 void Item::commonInit( bool loading ) {
390 	// unused: tex3d[0] = 0;
391 	// unused: textureInMemory = NULL;
392 	iconEffectTimer = 0;
393 	iconUnderEffectTimer = 0;
394 	for ( int i = 0; i < PARTICLE_COUNT; i++ ) {
395 		iconUnderEffectParticle[i] = new ParticleStruct();
396 		iconUnderEffectParticle[i]->life = -1;
397 		iconEffectParticle[i] = new ParticleStruct();
398 		iconEffectParticle[i]->life = -1;
399 	}
400 	identifiedBits = 0;
401 	missionId = missionObjectiveIndex = 0;
402 
403 	// --------------
404 	// regular attribs
405 
406 	weight = rpgItem->getWeight();
407 	quality = Util::pickOne( 50, 100 ); // starts out mostly healthy
408 
409 	int basePrice = ( this->spell ? this->spell->getExp() : rpgItem->getPrice() );
410 	price = basePrice + static_cast<int>( Util::getRandomSum( static_cast<float>( basePrice / 2 ), level ) );
411 
412 	// assign a spell to the item
413 	// the deeper you go, the more likely that items contain spells
414 	if ( rpgItem->hasSpell() &&
415 	        0 == Util::dice( MAX_MISSION_DEPTH - ( session->getCurrentMission() ? session->getCurrentMission()->getDepth() : 0 ) ) ) {
416 		this->spell = MagicSchool::getRandomSpell( 1 );
417 		price += static_cast<int>( Util::getRandomSum( static_cast<float>( basePrice / 2 ), this->spell->getLevel() ) );
418 	}
419 
420 	// --------------
421 	// magic attribs
422 
423 	// init to no-magic
424 	magicLevel = -1;
425 	bonus = 0;
426 	damageMultiplier = 1;
427 	monsterType = NULL;
428 	cursed = false;
429 	school = NULL;
430 	magicDamage = NULL;
431 	stateModSet = false;
432 	for ( int i = 0; i < StateMod::STATE_MOD_COUNT; i++ ) stateMod[i] = 0;
433 
434 	if ( rpgItem->isEnchantable() && !loading && session->getGameAdapter()->getCurrentDepth() > 0 ) {
435 		// roll for magic
436 		int n = Util::dice( static_cast<int>( 200.0f - ( level * 1.5f ) ) );
437 		if ( n < 5 && session->getGameAdapter()->getCurrentDepth() >= 4 ) enchant( Constants::DIVINE_MAGIC_ITEM );
438 		else if ( n < 10 && session->getGameAdapter()->getCurrentDepth() >= 3 ) enchant( Constants::CHAMPION_MAGIC_ITEM );
439 		else if ( n < 20 && session->getGameAdapter()->getCurrentDepth() >= 2 ) enchant( Constants::GREATER_MAGIC_ITEM );
440 		else if ( n < 30 && session->getGameAdapter()->getCurrentDepth() >= 1 ) enchant( Constants::LESSER_MAGIC_ITEM );
441 	}
442 
443 	// describe spell-holding items also
444 	if ( magicLevel < 0 && RpgItem::itemTypes[ rpgItem->getType() ].hasSpell ) {
445 		describeMagic( rpgItem->getDisplayName() );
446 	}
447 }
448 
449 /// Enchants the item.
450 
enchant(int newMagicLevel)451 void Item::enchant( int newMagicLevel ) {
452 	if ( magicLevel != -1 ) return;
453 
454 	magicLevel = newMagicLevel;
455 	if ( magicLevel >= Constants::MAGIC_ITEM_LEVEL_COUNT ) magicLevel = Constants::MAGIC_ITEM_LEVEL_COUNT - 1;
456 
457 	// item level caps the magic level:
458 	// 0-9: lesser
459 	// 10-19: greater
460 	// 20-29: champion
461 	// 39+ : divine
462 	// This is so low level items won't be too powerful.
463 	int maxMagicLevel = level / 10;
464 	if ( magicLevel > maxMagicLevel ) magicLevel = maxMagicLevel;
465 
466 	cursed = ( 0 == Util::dice( 20 ) );
467 
468 	// adjust the price
469 	price *= ( magicLevel + 2 );
470 
471 	int n;
472 	Spell *spell;
473 	switch ( magicLevel ) {
474 	case Constants::LESSER_MAGIC_ITEM:
475 		bonus = Util::pickOne( 1, 2 );
476 		if ( rpgItem->isWeapon() ) {
477 			damageMultiplier = Util::pickOne( 2, 3 );
478 			monsterType = Monster::getRandomMonsterType( level );
479 		}
480 		n = Util::pickOne( 2, 4 );
481 		for ( int i = 0; i < n; i++ ) {
482 			int skill = SkillGroup::stats->getRandomSkill()->getIndex();
483 			if ( skillBonus.find( skill ) == skillBonus.end() ) {
484 				skillBonus[skill] = Util::pickOne( 1, 2 );
485 			}
486 		}
487 		break;
488 	case Constants::GREATER_MAGIC_ITEM:
489 		bonus = Util::pickOne( 1, 3 );
490 		if ( rpgItem->isWeapon() ) {
491 			damageMultiplier = Util::pickOne( 2, 4 );
492 			monsterType = Monster::getRandomMonsterType( level );
493 		}
494 		spell = MagicSchool::getRandomSpell( 1 );
495 		if ( spell ) {
496 			school = spell->getSchool();
497 			magicDamage = new Dice( 1, Util::pickOne( 1, 3 ), Util::dice( 3 ) );
498 		}
499 		n = Util::pickOne( 2, 4 );
500 		for ( int i = 0; i < n; i++ ) {
501 			int skill = SkillGroup::stats->getRandomSkill()->getIndex();
502 			if ( skillBonus.find( skill ) == skillBonus.end() ) {
503 				skillBonus[skill] = Util::pickOne( 1, 3 );
504 			}
505 		}
506 		break;
507 	case Constants::CHAMPION_MAGIC_ITEM:
508 		bonus = Util::pickOne( 1, 4 );
509 		if ( rpgItem->isWeapon() ) {
510 			damageMultiplier = Util::pickOne( 2, 4 );
511 			monsterType = Monster::getRandomMonsterType( level );
512 		}
513 		spell = MagicSchool::getRandomSpell( 1 );
514 		if ( spell ) {
515 			school = spell->getSchool();
516 			magicDamage = new Dice( 1, Util::pickOne( 2, 4 ), Util::dice( 3 ) );
517 		}
518 		n = Util::pickOne( 1, 3 );
519 		if ( n > 0 ) stateModSet = true;
520 		for ( int i = 0; i < n; i++ ) {
521 			stateMod[ StateMod::getRandomGood()->getIndex() ] = 1;
522 		}
523 		n = Util::pickOne( 1, 3 );
524 		for ( int i = 0; i < n; i++ ) {
525 			int skill = SkillGroup::stats->getRandomSkill()->getIndex();
526 			if ( skillBonus.find( skill ) == skillBonus.end() ) {
527 				skillBonus[skill] = Util::pickOne( 1, 4 );
528 			}
529 		}
530 		break;
531 	case Constants::DIVINE_MAGIC_ITEM:
532 		bonus = Util::pickOne( 1, 5 );
533 		if ( rpgItem->isWeapon() ) {
534 			damageMultiplier = Util::pickOne( 2, 5 );
535 			monsterType = Monster::getRandomMonsterType( level );
536 		}
537 		spell = MagicSchool::getRandomSpell( 1 );
538 		if ( spell ) {
539 			school = spell->getSchool();
540 			magicDamage = new Dice( 1, Util::pickOne( 3, 5 ), Util::dice( 3 ) );
541 		}
542 		n = Util::pickOne( 1, 3 );
543 		if ( n > 0 ) stateModSet = true;
544 		for ( int i = 0; i < n; i++ ) {
545 			stateMod[ StateMod::getRandomGood()->getIndex() ] = 1;
546 		}
547 		n = Util::pickOne( 1, 3 );
548 		if ( n > 0 ) stateModSet = true;
549 		for ( int i = 0; i < n; i++ ) {
550 			stateMod[ StateMod::getRandomBad()->getIndex() ] = 2;
551 		}
552 		n = Util::pickOne( 2, 4 );
553 		for ( int i = 0; i < n; i++ ) {
554 			int skill = SkillGroup::stats->getRandomSkill()->getIndex();
555 			if ( skillBonus.find( skill ) == skillBonus.end() ) {
556 				skillBonus[skill] = Util::pickOne( 1, 5 );
557 			}
558 		}
559 		break;
560 	default:
561 		cerr << "*** Error: unknown magic level: " << magicLevel << endl;
562 	}
563 
564 	// turn off "vs. any creature"
565 	if ( !monsterType ) damageMultiplier = 1;
566 
567 	describeMagic( rpgItem->getDisplayName() );
568 }
569 
570 // max about 30 points (must be deterministic)
getMagicResistance()571 int Item::getMagicResistance() {
572 	return( 3 * ( ( getLevel() / 10 ) + getMagicLevel() ) );
573 }
574 
575 #define DEBUG_ITEM_ID 0
576 
577 /// Adds the magical properties, symbols etc. to the item's name.
578 
describeMagic(char const * displayName)579 void Item::describeMagic( char const* displayName ) {
580 
581 	// not for scrolls :-(
582 	if ( rpgItem->getType() == RpgItem::SCROLL ) return;
583 
584 	// e.g.: Lesser broadsword + 3 of nature magic
585 	enum { TMP_SIZE = 80 };
586 	char tmp[TMP_SIZE];
587 
588 
589 	if ( magicLevel > -1 ) {
590 		if ( DEBUG_ITEM_ID || isIdentified() ) {
591 			char format[1000];
592 			/* This represents the format of an identified item. Please do not translate the strings,
593 			only rearrange them to work with your language.
594 
595 			An example of the final string created by the code after parsing this message would be:
596 			"Ethereal Greater Protective Slaying Longsword (+4) of the Dragon"
597 			*/
598 			strcpy( format, _( "$spellsymbol $magiclevel $protective $slaying $itemname $bonus $symbol" ) );
599 			char *p = strtok( format, " " );
600 			strcpy( itemName, "" );
601 			while ( p ) {
602 
603 				if ( !strcmp( p, "$spellsymbol" ) ) {
604 					if ( RpgItem::itemTypes[ rpgItem->getType() ].hasSpell && spell ) {
605 						if ( strlen( itemName ) ) strcat( itemName, " " );
606 						strcat( itemName, spell->getSymbol() );
607 					}
608 				} else if ( !strcmp( p, "$magiclevel" ) ) {
609 					if ( magicLevel > -1 ) {
610 						if ( strlen( itemName ) ) strcat( itemName, " " );
611 						strcat( itemName, _( Constants::MAGIC_ITEM_NAMES[ magicLevel ] ) );
612 					}
613 				} else if ( !strcmp( p, "$protective" ) ) {
614 					if ( stateModSet ) {
615 						if ( strlen( itemName ) ) strcat( itemName, " " );
616 						strcat( itemName, _( "Protective" ) );
617 					}
618 				} else if ( !strcmp( p, "$slaying" ) ) {
619 					if ( damageMultiplier > 1 ) {
620 						if ( strlen( itemName ) ) strcat( itemName, " " );
621 						strcat( itemName, _( "Slaying" ) );
622 					}
623 					if ( strlen( itemName ) ) strcat( itemName, " " );
624 				} else if ( !strcmp( p, "$itemname" ) ) {
625 					if ( strlen( itemName ) ) strcat( itemName, " " );
626 					strcat( itemName, displayName );
627 				} else if ( !strcmp( p, "$bonus" ) ) {
628 					if ( bonus > 0 ) {
629 						if ( strlen( itemName ) ) strcat( itemName, " " );
630 						snprintf( tmp, TMP_SIZE, " (+%d)", bonus );
631 						strcat( itemName, tmp );
632 					}
633 				} else if ( !strcmp( p, "$symbol" ) ) {
634 					if ( skillBonus.size() > 0 ) {
635 						if ( school ) {
636 							if ( strlen( itemName ) ) strcat( itemName, " " );
637 							strcat( itemName, school->getSymbol() );
638 						} else if ( stateModSet ) {
639 							bool stateModFound = false;
640 							for ( int i = 0; i < StateMod::STATE_MOD_COUNT; i++ ) {
641 								if ( stateMod[ i ] > 0 ) {
642 									if ( strlen( itemName ) ) strcat( itemName, " " );
643 									strcat( itemName, StateMod::stateMods[ i ]->getSymbol() );
644 									stateModFound = true;
645 									break;
646 								}
647 							}
648 							if ( !stateModFound ) {
649 								// use the first skill as the noun
650 								map<int, int>::iterator i = skillBonus.begin();
651 								int skill = i->first;
652 								if ( strlen( itemName ) ) strcat( itemName, " " );
653 								strcat( itemName, Skill::skills[ skill ]->getSymbol() );
654 							}
655 						}
656 					}
657 				} else if ( *p != '$' ) {
658 					if ( strlen( itemName ) ) strcat( itemName, " " );
659 					strcat( itemName, p );
660 				}
661 				p = strtok( NULL, " " );
662 			}
663 		} else {
664 			snprintf( itemName, ITEM_NAME_SIZE, "??? %s ???", displayName );
665 		}
666 	} else {
667 		strcpy( itemName, displayName );
668 	}
669 }
670 
671 /// Is this a special (mission or story related) item?
672 
isSpecial()673 bool Item::isSpecial() {
674 	return getRpgItem()->isSpecial();
675 }
676 
677 /// Rolls additional magical damage.
678 
rollMagicDamage()679 int Item::rollMagicDamage() {
680 	return ( magicDamage ? magicDamage->roll() : 0 );
681 }
682 
683 /// Returns the bonus damage (e.g. "+3") as a string.
684 
describeMagicDamage()685 char *Item::describeMagicDamage() {
686 	return ( magicDamage ? magicDamage->toString() : NULL );
687 }
688 
debugMagic(char * s)689 void Item::debugMagic( char *s ) {
690 	RpgItem *item = getRpgItem();
691 	cerr << s << endl;
692 	cerr << "Magic item: " << item->getName() << "(+" << bonus << ")" << endl;
693 	cerr << "\tdamageMultiplier=" << damageMultiplier << " vs. monsterType=" << ( monsterType ? monsterType : "null" ) << endl;
694 	cerr << "\tSchool: " << ( school ? school->getName() : "null" ) << endl;
695 	cerr << "\tstate mods:" << endl;
696 	for ( int i = 0; i < StateMod::STATE_MOD_COUNT; i++ ) {
697 		if ( this->isStateModSet( i ) ) cerr << "set: " << StateMod::stateMods[i]->getDisplayName() << endl;
698 		if ( this->isStateModProtected( i ) ) cerr << "protected: " << StateMod::stateMods[i]->getDisplayName() << endl;
699 	}
700 	cerr << "\tskill bonuses:" << endl;
701 	for ( map<int, int>::iterator i = skillBonus.begin(); i != skillBonus.end(); ++i ) {
702 		int skill = i->first;
703 		int bonus = i->second;
704 		cerr << "\t\t" << Skill::skills[skill]->getDisplayName() << " +" << bonus << endl;
705 	}
706 	cerr << "-----------" << endl;
707 }
708 
709 /// Sets the number of remaining charges/uses.
710 
setCurrentCharges(int n)711 void Item::setCurrentCharges( int n ) {
712 	if ( n < 0 ) n = 0;
713 	if ( n > rpgItem->getMaxCharges() )
714 		n = rpgItem->getMaxCharges();
715 	currentCharges = n;
716 }
717 
718 /// Sets the item's attached spell. Can create spell scrolls.
719 
setSpell(Spell * spell)720 void Item::setSpell( Spell *spell ) {
721 	this->spell = spell;
722 	if ( getRpgItem()->getType() == RpgItem::SCROLL ) {
723 		snprintf( itemName, ITEM_NAME_SIZE, _( "Scroll of %s" ), spell->getDisplayName() );
724 	} else {
725 		describeMagic( rpgItem->getDisplayName() );
726 	}
727 }
728 
729 /// The item's localized name.
730 
getName()731 const char *Item::getName() {
732 	return getItemName();
733 }
734 
735 /// Which icon tile from tiles.png to use? (deprecated)
736 
getIconTileX()737 int Item::getIconTileX() {
738 	return rpgItem->getIconTileX();
739 }
740 
741 /// Which icon tile from tiles.png to use? (deprecated)
742 
getIconTileY()743 int Item::getIconTileY() {
744 	return rpgItem->getIconTileY();
745 }
746 
747 /// Max shooting distance for ranged weapons.
748 
getRange()749 int Item::getRange() {
750 	return getRpgItem()->getRange();
751 }
752 
753 /// Items are always storable (in a quickspell slot).
754 
getStorableType()755 int Item::getStorableType() {
756 	return Storable::ITEM_STORABLE;
757 }
758 
isStorable()759 const char *Item::isStorable() {
760 	return NULL;
761 }
762 
763 /// The type of item (weapon, armor, special etc.).
764 
getType()765 char const* Item::getType() {
766 	// how is an item saved in a map? (this is not displayName)
767 	return getRpgItem()->getName();
768 }
769 
770 /// Tries to identify an unidentified item property.
771 
772 /// Sets identification bit to true if random function eveluates more than infoDetailLevel
773 /// with specified cap modifier.
774 /// @param bit
775 /// @param modifier
776 /// @param infoDetailLevel
777 
trySetIDBit(int bit,float modifier,int infoDetailLevel)778 void Item::trySetIDBit( int bit, float modifier, int infoDetailLevel ) {
779 	//If not yet set
780 	if ( !getIdentifiedBit( bit ) ) {
781 		if ( infoDetailLevel > static_cast<int>( Util::roll( 0.0f, modifier ) ) ) {
782 			setIdentifiedBit( bit, true );
783 		} else {
784 			setIdentifiedBit( bit, false );
785 		}
786 	}
787 }
788 
789 /// Tries to identify unidentified item properties.
790 
791 /// A higher value of infoDetailLevel decreases the success
792 /// rate.
793 
identify(int infoDetailLevel)794 void Item::identify( int infoDetailLevel ) {
795 #ifdef DEBUG_IDENTIFY_ITEM
796 	infoDetailLevel = 500;
797 #endif
798 	//identifiedBits = (Uint32)0x0000;
799 	if ( isMagicItem() ) {
800 		trySetIDBit( Item::ID_BONUS, 100.0f, infoDetailLevel );
801 		if ( getDamageMultiplier() > 1 ) {
802 			trySetIDBit( Item::ID_DAMAGE_MUL, 100.0f, infoDetailLevel );
803 		} else {
804 			setIdentifiedBit( Item::ID_DAMAGE_MUL, true );
805 		}
806 		if ( getSchool() ) {
807 			trySetIDBit( Item::ID_MAGIC_DAMAGE, 100.0f, infoDetailLevel );
808 		} else {
809 			setIdentifiedBit( Item::ID_MAGIC_DAMAGE, true );
810 		}
811 
812 		bool found = false;
813 		for ( int i = 0; i < StateMod::STATE_MOD_COUNT; i++ ) {
814 			if ( isStateModSet( i ) ) {
815 				found = true;
816 				break;
817 			}
818 		}
819 		if ( found ) {
820 			trySetIDBit( Item::ID_STATE_MOD, 100.0f, infoDetailLevel );
821 		} else {
822 			setIdentifiedBit( Item::ID_STATE_MOD, true );
823 		}
824 
825 		found = false;
826 		for ( int i = 0; i < StateMod::STATE_MOD_COUNT; i++ ) {
827 			if ( isStateModProtected( i ) ) {
828 				found = true;
829 				break;
830 			}
831 		}
832 		if ( found ) {
833 			trySetIDBit( Item::ID_PROT_STATE_MOD, 100.0f, infoDetailLevel );
834 		} else {
835 			setIdentifiedBit( Item::ID_PROT_STATE_MOD, true );
836 		}
837 
838 		found = false;
839 		map<int, int> *skillBonusMap = getSkillBonusMap();
840 		for ( map<int, int>::iterator i = skillBonusMap->begin(); i != skillBonusMap->end(); ++i ) {
841 			found = true;
842 			break;
843 		}
844 		if ( found ) {
845 			trySetIDBit( Item::ID_SKILL_BONUS, 100.0f, infoDetailLevel );
846 		} else {
847 			setIdentifiedBit( Item::ID_SKILL_BONUS, true );
848 		}
849 
850 		// cursed is hard to detect
851 		if ( isCursed() ) {
852 			trySetIDBit( Item::ID_SKILL_BONUS, 200.0f, infoDetailLevel );
853 		} else {
854 			setIdentifiedBit( Item::ID_CURSED, true );
855 		}
856 
857 		if ( isIdentified() ) {
858 			describeMagic( rpgItem->getDisplayName() );
859 			session->getGameAdapter()->writeLogMessage( _( "An item was fully identified!" ) );
860 			// update ui
861 			session->getGameAdapter()->refreshBackpackUI();
862 		}
863 	} else {
864 		//No need for identification - item not magical
865 		identifiedBits = ( Uint32 )0xffff;
866 	}
867 	// fprintf( stderr, "skill=%d id=%x\n", infoDetailLevel, identifiedBits );
868 }
869 
870 /// Backpack x size.
871 
getBackpackWidth()872 int Item::getBackpackWidth() {
873 	return ( getShape()->getIcon().isSpecified() ? getShape()->getIconWidth() : rpgItem->getBackpackWidth() );
874 }
875 
876 /// Backpack y size.
877 
getBackpackHeight()878 int Item::getBackpackHeight() {
879 	return ( getShape()->getIcon().isSpecified() ? getShape()->getIconHeight() : rpgItem->getBackpackHeight() );
880 }
881 
renderIcon(Scourge * scourge,SDL_Rect * rect,int gridSize,bool smallIcon)882 void Item::renderIcon( Scourge *scourge, SDL_Rect *rect, int gridSize, bool smallIcon ) {
883 	int iw = getBackpackWidth() * gridSize;
884 	int ih = getBackpackHeight() * gridSize;
885 
886 	int iy = rect->y;
887 	if ( rect->h - ih > gridSize ) iy += rect->h - ih - gridSize;
888 	renderIcon( scourge, rect->x + ( rect->w - iw ) / 2, iy, iw, ih, smallIcon );
889 }
890 
891 /// Renders the item's icon and any overlaid effects
renderIcon(Scourge * scourge,int x,int y,int w,int h,bool smallIcon)892 void Item::renderIcon( Scourge *scourge, int x, int y, int w, int h, bool smallIcon ) {
893 	Texture tex;
894 	int rw, rh, ox, oy, iw, ih;
895 	// getItemIconInfo( &tex, &rw, &rh, &ox, &oy, &iw, &ih, w, h, smallIcon );
896 	getItemIconInfo( &tex, &rw, &rh, &ox, &oy, &iw, &ih, w, h, false );
897 	glPushMatrix();
898 	glTranslatef( x + ox, y + oy, 0 );
899 // if ( !smallIcon ) {
900 	if ( w > 0 && h > 0 ) glScalef( rw / static_cast<float>( w ), rh / static_cast<float>( h ), 1 );
901 	if ( isMagicItem() ) {
902 		renderUnderItemIconEffect( scourge, 0, 0, rw, rh, iw, ih );
903 	}
904 // }
905 	// renderItemIcon( scourge, 0, 0, rw, rh, smallIcon );
906 	renderItemIcon( scourge, 0, 0, rw, rh, false );
907 // if ( !smallIcon ) {
908 	if ( isMagicItem() ) {
909 		renderItemIconEffect( scourge, 0, 0, rw, rh, iw, ih );
910 		renderItemIconIdentificationEffect( scourge, 0, 0, rw, rh );
911 	}
912 	glScalef( 1, 1, 1 );
913 // }
914 	glPopMatrix();
915 }
916 
917 /// Returns the item's icon with additional info.
918 
919 /// The following is returned: The OpenGL texture, width and height of the texture,
920 /// and the top left corner of the item graphic within the texture.
921 /// When smallIcon is false, it returns the backpack graphic, else the small icon.
922 
getItemIconInfo(Texture * texp,int * rwp,int * rhp,int * oxp,int * oyp,int * iw,int * ih,int w,int h,bool smallIcon)923 void Item::getItemIconInfo( Texture* texp, int *rwp, int *rhp, int *oxp, int *oyp, int *iw, int *ih, int w, int h, bool smallIcon ) {
924 	Texture tex;
925 	int rw, rh, ox, oy;
926 	if ( !smallIcon && getShape()->getIcon().isSpecified() ) {
927 		tex = getShape()->getIcon();
928 		*iw = getShape()->getIconWidth() * 32;
929 		*ih = getShape()->getIconHeight() * 32;
930 		if ( getShape()->getIconWidth() > getShape()->getIconHeight() ) {
931 			rw = w;
932 			rh = static_cast<int>( getShape()->getIconHeight() * rw / static_cast<float>( getShape()->getIconWidth() ) );
933 			ox = 0;
934 			oy = ( h - rh ) / 2;
935 		} else {
936 			rh = h;
937 			rw = static_cast<int>( getShape()->getIconWidth() * rh / static_cast<float>( getShape()->getIconHeight() ) );
938 			oy = 0;
939 			ox = ( w - rw ) / 2;
940 		}
941 	} else {
942 		tex = session->getShapePalette()->tilesTex[ getRpgItem()->getIconTileX() ][ getRpgItem()->getIconTileY() ];
943 		*iw = w;
944 		*ih = h;
945 		rw = w;
946 		rh = h;
947 		ox = oy = 0;
948 	}
949 	*texp = tex;
950 	*rwp = rw;
951 	*rhp = rh;
952 	*oxp = ox;
953 	*oyp = oy;
954 }
955 
956 /// Renders the item's icon (in lists, the backpack etc.)
957 
renderItemIcon(Scourge * scourge,int x,int y,int w,int h,bool smallIcon)958 void Item::renderItemIcon( Scourge *scourge, int x, int y, int w, int h, bool smallIcon ) {
959 	glColor4f( 1, 1, 1, 1 );
960 	glEnable( GL_BLEND );
961 	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
962 	glEnable( GL_TEXTURE_2D );
963 	getItemIconTexture( smallIcon ).glBind();
964 	glBegin( GL_TRIANGLE_STRIP );
965 	glTexCoord2d( 0, 0 );
966 	glVertex2d( x, y );
967 	glTexCoord2d( 1, 0 );
968 	glVertex2d( x + w, y );
969 	glTexCoord2d( 0, 1 );
970 	glVertex2d( x, y + h );
971 	glTexCoord2d( 1, 1 );
972 	glVertex2d( x + w, y + h );
973 	glEnd();
974 	glDisable( GL_BLEND );
975 }
976 
977 /// Returns the item's icon texture.
978 
getItemIconTexture(bool smallIcon)979 Texture Item::getItemIconTexture( bool smallIcon ) {
980 	return ( !smallIcon && getShape()->getIcon().isSpecified() ? getShape()->getIcon() :
981 	         session->getShapePalette()->tilesTex[ getRpgItem()->getIconTileX() ][ getRpgItem()->getIconTileY() ] );
982 }
983 
984 /// Creates an icon texture from a 3D view of the item.
985 
986 /* unused
987 void Item::create3dTex( Scourge *scourge, float w, float h ) {
988  if ( textureInMemory ) return;
989 
990  // clear the error flags
991  Util::getOpenGLError();
992 
993  // Create texture and copy minimap date from backbuffer on it
994  unsigned int textureSizeW = 32;
995  unsigned int textureSizeH = 32;
996  textureInMemory = ( unsigned char * ) malloc( textureSizeW * textureSizeH * 4 );
997 
998  glPushAttrib( GL_ALL_ATTRIB_BITS );
999 
1000  glDisable( GL_CULL_FACE );
1001  glEnable( GL_DEPTH_TEST );
1002  glDepthMask( GL_TRUE );
1003  glEnable( GL_TEXTURE_2D );
1004  glDisable( GL_BLEND );
1005  glDisable( GL_SCISSOR_TEST );
1006  glDisable( GL_STENCIL_TEST );
1007 
1008  glGenTextures( 1, tex3d );
1009  glBindTexture( GL_TEXTURE_2D, tex3d[0] );
1010  glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
1011  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );                                      // filtre appliqu� a la texture
1012  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1013  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
1014  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
1015  glTexImage2D( GL_TEXTURE_2D, 0, ( scourge->getPreferences()->getBpp() > 16 ? GL_RGBA : GL_RGBA4 ), textureSizeW, textureSizeH, 0,
1016                GL_RGBA, GL_UNSIGNED_BYTE, textureInMemory );
1017  fprintf( stderr, "OpenGl result for item(%s) glTexImage2D : %s\n", getName(), Util::getOpenGLError() );
1018 
1019  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1020  glClearColor( 0, 0, 0, 0 );
1021  glClearDepth( 1 );
1022 
1023  glScalef( ( this->getShape()->getWidth() ) / w,
1024            ( this->getShape()->getHeight() ) / h,
1025            1 );
1026  glPushMatrix();
1027  glLoadIdentity();
1028  this->getShape()->rotateIcon();
1029  glColor4f( 1, 1, 1, 1 );
1030  this->getShape()->draw();
1031  glPopMatrix();
1032  glScalef( 1, 1, 1 );
1033 
1034  //SDL_GL_SwapBuffers( );
1035 
1036 
1037  // Copy to a texture
1038  glPushMatrix();
1039  glLoadIdentity();
1040  glBindTexture( GL_TEXTURE_2D, tex3d[0] );
1041  glCopyTexSubImage2D(
1042    GL_TEXTURE_2D,
1043    0,      // MIPMAP level
1044    0,      // x texture offset
1045    0,      // y texture offset
1046    0,              // x window coordinates
1047    scourge->getScreenHeight() - textureSizeH,   // y window coordinates
1048    textureSizeW,    // width
1049    textureSizeH     // height
1050  );
1051  glPopMatrix();
1052  fprintf( stderr, "OpenGl result for item(%s) glCopyTexSubImage2D: %s\n", getName(), Util::getOpenGLError() );
1053 
1054  glDisable( GL_TEXTURE_2D );
1055  glPopAttrib();
1056 }
1057 */
1058 
renderUnderItemIconEffect(Scourge * scourge,int x,int y,int w,int h,int iw,int ih)1059 void Item::renderUnderItemIconEffect( Scourge *scourge, int x, int y, int w, int h, int iw, int ih ) {
1060 	Uint32 t = SDL_GetTicks();
1061 	if ( t - iconUnderEffectTimer > 5 ) {
1062 		iconUnderEffectTimer = t;
1063 		for ( int i = 0; i < PARTICLE_COUNT; i++ ) {
1064 			if ( iconUnderEffectParticle[i]->life < 0 ||
1065 			        iconUnderEffectParticle[i]->life >= iconUnderEffectParticle[i]->maxLife ) {
1066 				iconUnderEffectParticle[i]->life = 0;
1067 				iconUnderEffectParticle[i]->maxLife = Util::pickOne( 30, 59 );
1068 				iconUnderEffectParticle[i]->zoom = Util::roll( 10.0f, 15.0f );
1069 				iconUnderEffectParticle[i]->x = Util::roll( 0.0f, w / 4.0f ) + ( w * 0.375f );
1070 				iconUnderEffectParticle[i]->y = Util::roll( 0.0f, h / 4.0f ) + ( h * 0.375f );
1071 				iconUnderEffectParticle[i]->z = 0;
1072 			}
1073 			iconUnderEffectParticle[i]->zoom += 1.0f;
1074 			//iconUnderEffectParticle[i]->y += 0.25f;
1075 			iconUnderEffectParticle[i]->life++;
1076 		}
1077 	}
1078 	glEnable( GL_TEXTURE_2D );
1079 	scourge->getSession()->getShapePalette()->getNamedTexture( "flame" ).glBind();
1080 	glEnable( GL_BLEND );
1081 	//glBlendFunc( GL_ONE, GL_ONE );
1082 	glBlendFunc( GL_SRC_ALPHA, GL_ONE );
1083 	//glBlendFunc( GL_SRC_ALPHA, GL_ONE );
1084 	//glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
1085 	for ( int i = 0; i < PARTICLE_COUNT; i++ ) {
1086 		float a = ( 1 - iconUnderEffectParticle[i]->life / static_cast<float>( iconUnderEffectParticle[i]->maxLife ) ) / 8.0f;
1087 		glColor4f( Constants::MAGIC_ITEM_COLOR[ getMagicLevel() ]->r * a,
1088 		           Constants::MAGIC_ITEM_COLOR[ getMagicLevel() ]->g * a,
1089 		           Constants::MAGIC_ITEM_COLOR[ getMagicLevel() ]->b * a,
1090 		           0.5f );
1091 		glPushMatrix();
1092 		glTranslatef( x, y, 0 );
1093 		glTranslatef( iconUnderEffectParticle[i]->x - iconUnderEffectParticle[i]->zoom / 2,
1094 		              iconUnderEffectParticle[i]->y - iconUnderEffectParticle[i]->zoom / 2, 0 );
1095 		//glRotatef( iconUnderEffectParticle[i]->life, 0, 0, 1 );
1096 		glBegin( GL_TRIANGLE_STRIP );
1097 		glTexCoord2d( 0, 0 );
1098 		glVertex2d( 0, 0 );
1099 		glTexCoord2d( 1, 0 );
1100 		glVertex2d( iconUnderEffectParticle[i]->zoom, 0 );
1101 		glTexCoord2d( 0, 1 );
1102 		glVertex2d( 0, iconUnderEffectParticle[i]->zoom );
1103 		glTexCoord2d( 1, 1 );
1104 		glVertex2d( iconUnderEffectParticle[i]->zoom, iconUnderEffectParticle[i]->zoom );
1105 		glEnd();
1106 		glPopMatrix();
1107 	}
1108 	glDisable( GL_BLEND );
1109 	glColor4f( 1, 1, 1, 1 );
1110 }
1111 
1112 /// Renders the "blinking stars" effect for magical items.
1113 
renderItemIconEffect(Scourge * scourge,int x,int y,int w,int h,int iw,int ih)1114 void Item::renderItemIconEffect( Scourge *scourge, int x, int y, int w, int h, int iw, int ih ) {
1115 	int particleCount = 3 * ( getMagicLevel() + 1 );
1116 	// draw an effect
1117 	Uint32 t = SDL_GetTicks();
1118 	if ( t - iconEffectTimer > 5 ) {
1119 		iconEffectTimer = t;
1120 		for ( int i = 0; i < particleCount; i++ ) {
1121 			if ( iconEffectParticle[i]->life < 0 ||
1122 			        iconEffectParticle[i]->life >= iconEffectParticle[i]->maxLife ) {
1123 				iconEffectParticle[i]->life = 0;
1124 				iconEffectParticle[i]->maxLife = Util::pickOne( 30, 59 );
1125 				iconEffectParticle[i]->zoom = 0.5f;
1126 				iconEffectParticle[i]->x = static_cast<float>( w ) * Util::mt_rand();
1127 				iconEffectParticle[i]->y = static_cast<float>( h ) * Util::mt_rand();
1128 				iconEffectParticle[i]->z = 0;
1129 			}
1130 			iconEffectParticle[i]->zoom += ( iconEffectParticle[i]->life >= iconEffectParticle[i]->maxLife / 2.0f ? -1 : 1 ) * 0.5f;
1131 			//iconEffectParticle[i]->y += 0.25f;
1132 			iconEffectParticle[i]->life++;
1133 		}
1134 	}
1135 	glEnable( GL_TEXTURE_2D );
1136 	scourge->getSession()->getShapePalette()->getNamedTexture( "bling" ).glBind();
1137 	glEnable( GL_BLEND );
1138 	//glBlendFunc( GL_ONE, GL_ONE );
1139 	glBlendFunc( GL_SRC_ALPHA, GL_ONE );
1140 	//glBlendFunc( GL_SRC_ALPHA, GL_ONE );
1141 	//glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
1142 	for ( int i = 0; i < particleCount; i++ ) {
1143 		float a = ( iconEffectParticle[i]->life / static_cast<float>( iconEffectParticle[i]->maxLife ) );
1144 		if ( a >= 0.5 ) a = 1 - a;
1145 		a = a * 2.0f;
1146 		glColor4f( Constants::MAGIC_ITEM_COLOR[ getMagicLevel() ]->r * a,
1147 		           Constants::MAGIC_ITEM_COLOR[ getMagicLevel() ]->g * a,
1148 		           Constants::MAGIC_ITEM_COLOR[ getMagicLevel() ]->b * a,
1149 		           1 );
1150 		glPushMatrix();
1151 		glTranslatef( iconEffectParticle[i]->x - iconEffectParticle[i]->zoom / 2,
1152 		              iconEffectParticle[i]->y - iconEffectParticle[i]->zoom / 2, 0 );
1153 		if ( getMagicLevel() >= Constants::DIVINE_MAGIC_ITEM ) {
1154 			glRotatef( 360.0f * a, 0, 0, 1 );
1155 		}
1156 		glBegin( GL_TRIANGLE_STRIP );
1157 		glTexCoord2d( 0, 0 );
1158 		glVertex2d( x, y );
1159 		glTexCoord2d( 1, 0 );
1160 		glVertex2d( x + iconEffectParticle[i]->zoom, y );
1161 		glTexCoord2d( 0, 1 );
1162 		glVertex2d( x, y + iconEffectParticle[i]->zoom );
1163 		glTexCoord2d( 1, 1 );
1164 		glVertex2d( x + iconEffectParticle[i]->zoom, y + iconEffectParticle[i]->zoom );
1165 		glEnd();
1166 		glPopMatrix();
1167 	}
1168 	glDisable( GL_BLEND );
1169 	glColor4f( 1, 1, 1, 1 );
1170 }
1171 
1172 /// Renders the effect for unidentified items. Currently only prints "?"
1173 
renderItemIconIdentificationEffect(Scourge * scourge,int x,int y,int w,int h)1174 void Item::renderItemIconIdentificationEffect( Scourge *scourge, int x, int y, int w, int h ) {
1175 	if ( isIdentified() ) {
1176 		/*
1177 		glDisable( GL_TEXTURE_2D );
1178 		glColor4f( Constants::MAGIC_ITEM_COLOR[ getMagicLevel() ]->r,
1179 		      Constants::MAGIC_ITEM_COLOR[ getMagicLevel() ]->g,
1180 		      Constants::MAGIC_ITEM_COLOR[ getMagicLevel() ]->b,
1181 		      1 );
1182 		glBegin( GL_LINE_LOOP );
1183 		glVertex2d( x + 1, y + h - 1 );
1184 		glVertex2d( x + 1, y + 1 );
1185 		glVertex2d( x + w - 1, y + 1 );
1186 		glVertex2d( x + w - 1, y + h - 1 );
1187 		glEnd();
1188 		glEnable( GL_TEXTURE_2D );
1189 		glColor4f( 1, 1, 1, 1 );
1190 		*/
1191 	} else {
1192 		scourge->getSDLHandler()->texPrint( x + 2, y + 12, "?" );
1193 	}
1194 }
1195 
1196 /// Item's tooltip text (for the backpack).
1197 
getTooltip(char * tooltip)1198 void Item::getTooltip( char *tooltip ) {
1199 	enum { TMP_SIZE = 500 };
1200 	char tmp[ TMP_SIZE ];
1201 	strcpy( tooltip, getName() );
1202 	if ( getRpgItem()->isWeapon() ) {
1203 		snprintf( tmp, TMP_SIZE, "|%s:%d%%(%c)",
1204 		          _( "DAM" ), getRpgItem()->getDamage(),
1205 		          RpgItem::getDamageTypeLetter( getRpgItem()->getDamageType() ) );
1206 		strcat( tooltip, tmp );
1207 		if ( getRpgItem()->getAP() > 0 ) {
1208 			snprintf( tmp, TMP_SIZE, " %s:%d", _( "AP" ), getRpgItem()->getAP() );
1209 			strcat( tooltip, tmp );
1210 		}
1211 		if ( getRange() > MIN_DISTANCE ) {
1212 			snprintf( tmp, TMP_SIZE, " %s:%d", _( "RANGE" ), getRange() );
1213 			strcat( tooltip, tmp );
1214 		}
1215 	} else if ( getRpgItem()->isArmor() ) {
1216 		strcat( tooltip, "|" );
1217 		strcat( tooltip, _( "DEF" ) );
1218 		strcat( tooltip, ":" );
1219 		for ( int i = 0; i < RpgItem::DAMAGE_TYPE_COUNT; i++ ) {
1220 			snprintf( tmp, TMP_SIZE, _( " %d(%c)" ),
1221 			          getRpgItem()->getDefense( i ),
1222 			          RpgItem::getDamageTypeLetter( i ) );
1223 			strcat( tooltip, tmp );
1224 		}
1225 	}
1226 	if ( getLevel() > 1 ) {
1227 		snprintf( tmp, TMP_SIZE, "|%s:%d",
1228 		          _( "Level" ), getLevel() );
1229 		strcat( tooltip, tmp );
1230 	}
1231 	if ( getRpgItem()->getPotionPower() ) {
1232 		snprintf( tmp, TMP_SIZE, "|%s:%d", _( "Power" ), getRpgItem()->getPotionPower() );
1233 		strcat( tooltip, tmp );
1234 	}
1235 	if ( getRpgItem()->getMaxCharges() > 0 &&
1236 	        ( !getRpgItem()->hasSpell() || getSpell() ) ) {
1237 		snprintf( tmp, TMP_SIZE, "|%s:%d(%d)", _( "Charges" ), getCurrentCharges(), getRpgItem()->getMaxCharges() );
1238 		strcat( tooltip, tmp );
1239 		if ( getSpell() ) {
1240 			snprintf( tmp, TMP_SIZE, "|%s:%s", _( "Spell" ), getSpell()->getDisplayName() );
1241 			strcat( tooltip, tmp );
1242 		}
1243 	}
1244 	if ( getRpgItem()->getPotionTime() > 0 ) {
1245 		snprintf( tmp, TMP_SIZE, "|%s:%d", _( "Duration" ), getRpgItem()->getPotionTime() );
1246 		strcat( tooltip, tmp );
1247 	}
1248 }
1249 
isFullyIdentified()1250 bool Item::isFullyIdentified() {
1251 
1252 	bool id = true;
1253 
1254 	if ( magicLevel > -1 ) {
1255 		if ( DEBUG_ITEM_ID || isIdentified() ) {
1256 			id = true;
1257 		} else {
1258 			id = false;
1259 		}
1260 	}
1261 
1262 	return id;
1263 }
1264 
getContainerTexture()1265 Texture Item::getContainerTexture() {
1266 	if( !containerTexture.isSpecified() ) {
1267 		containerTexture.load( getRpgItem()->getContainerTexture() );
1268 	}
1269 	return containerTexture;
1270 }
1271