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