JNR
laPlayer.cpp
1 /*
2 
3 Jump'n'Run Engine
4 http://www.atanaslaskov.com/jnr/
5 
6 BSD LICENSE
7 Copyright (c) 2007-2013, Atanas Laskov
8 All rights reserved.
9 
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions are met:
12 1. Redistributions of source code must retain the above copyright notice,
13 this list of conditions and the following disclaimer.
14 2. Redistributions in binary form must reproduce the above copyright notice,
15 this list of conditions and the following disclaimer in the documentation
16 and/or other materials provided with the distribution.
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL ATANAS LASKOV BE LIABLE FOR ANY
21 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 */
29 
30 //
31 // FILE: laPlayer.cpp
32 //
33 // Playable character class
34 //
35 // Copyright (C) 2007-2013 Atanas Laskov, <latanas@gmail.com>
36 //
37 #include "stdafx.h"
38 #include "Core-Level-JR.h"
39 
40 #include "AI\ai.h"
41 #include "AI\aiNPCDialog.h"
42 
43 // Constructor
44 //
45 laPlayer::laPlayer(rpgSheet* pSheet):
46  laFightingCreature( pSheet ? pSheet : new rpgSheet_PlayableChar() ),
47  _uiInventoryWindow( &( ((rpgSheet_PlayableChar*)getSheet())->properties()->items ) /*&_inventory*/ ),
48  _uiPotionsWindow( &( ((rpgSheet_PlayableChar*)getSheet())->properties()->potions ) )
49 {
50  _uiPotionsWindow.toggleHorizontal(M_FALSE);
51  _uiPotionsWindow.toggleSelection(M_FALSE);
52 
53  _ttTurn.parameters(0.2);
54 
55  // Battle zoom mode
56  //
57  laSettings* ps = laSystemIntegrator::getSettings();
58  enableBattleZoom(ps->gp_enable_battle_zoom);
59  _bBattleMode = M_TRUE;
60 
61  getLevelObject()->zoom(1.8);
62 
63  _ttBattleStance.parameters(1, 1, M_FALSE);
64  _ttBattleStance.force();
65 
66  _ptPivot.size.y( M_UNIT );
67 
68  //Initialize time triggers
69  //
70  _ttAttack.parameters(M_PLAYER_ATTACK_INTERVAL);
71  _ttAttack.enable(M_FALSE);
72 
73  shutterDeath.parameters( laColor(), 2);
74  shutterHit.parameters( laColor(200,0,0,100), 0.5, M_FALSE, M_TRUE);
75 
76  // Misc
77  fxMessages.parameters(2.5*M_UNIT, 2, M_UNIT*0.22); // slightly bigger messages for the player
78  _pActiveTrap = NULL;
79 
80  _pNPCDialog = NULL;
81 
82  //_bPeacefulMode = M_FALSE;
83  _ttPeacefulAction.parameters(1);
84  _ttPeacefulAction.enable();
85  _ttPeacefulAction.force();
86 
87  _nLastWeapon = 0;
88  _pwPeace = NULL;
89 }
90 
91 laPlayer::~laPlayer(void)
92 {
93  delete _pwPeace;
94 }
95 
96 // Respawn character
97 //
98 void laPlayer::respawn()
99 {
100  ERRORLEVEL_BEGIN;
101 
102  _pSheet->bResetProperties = M_FALSE; // reset only modifiers
103  laFightingCreature::respawn();
104 
105  rpgSheet_PlayableChar *psh = ((rpgSheet_PlayableChar*)_pSheet);
106  psh->properties()->nHP = psh->properties()->nHP_Max;
107 
108  // Save curent state of the RPG sheet in the profile
109  // NOTE: Important to save before laFightingCreature::respawn(), in order to keep XP
110  //
111  // TODO: load_inventory(), in order to restore to the start of level state, before saving
112  this->getLevelObject()->forceSave();
113 
114  laSettings* ps = laSystemIntegrator::getSettings();
115  enableBattleZoom(ps->gp_enable_battle_zoom);
116 
117  //Tweak pivot
118  _ptPivot.x( _ptPivot.x() + M_UNIT/2 );
119 
120  // Add the "Peaceful" weapon
121  // i.e. attacks disabled when this item is active
122  //
123  rpgInventory* pInv = & ( ((rpgSheet_PlayableChar*)getSheet())->properties()->items );
124  pInv->add( _pwPeace );
125 
126  ERRORLEVEL_END;
127 }
128 
129 void laPlayer::_load_profile(char* strName)
130 {
131  ERRORLEVEL_BEGIN;
132  if(strName[0] == '\0') return; //NOTE: No profile, ignore the load;
133 
134 
135  char strfile[64];
136  sprintf(strfile, "save\\%s.profile", strName);
137  laFileParser fp;
138 
139  if( fp.fileOpen(strfile) )
140  {
141  try{
142  laPropertyList pl;
143  fp.readObj(&pl, M_FALSE);
144 
145  _pSheet->load( pl.getChild("rpg-sheet") );
146  //TODO: load inventory
147  //TODO: load skills
148  }
149  catch(laError&) { MLOG("Defaulting to Level 1 RPG sheet for the player object."); };
150  }
151 
152  // Else keep default RPG props loaded from the TS
153 
154  ERRORLEVEL_END;
155 }
156 
157 void laPlayer::saveProfile(FILE* f)
158 {
159  ERRORLEVEL_BEGIN;
160 
161  ((rpgSheet_PlayableChar*)_pSheet)->properties()->save(f);
162  //TODO: Save inventory
163  //TODO: Save skills
164 
165  ERRORLEVEL_END;
166 }
167 
168 
169 // Load from level file
170 //
171 void laPlayer::load(class laFileParser *fp)
172 {
173  ERRORLEVEL_BEGIN;
174 
175  //laSettings* ps = laSystemIntegrator::getSettings();
176  //enableBattleZoom(ps->gp_enable_battle_zoom);
177 
178  laFightingCreature::load(fp);
179  _ptPivot.x( _ptPivot.x() + M_UNIT/2 );
180 
181  // Read property sheet form the save file
182  //
183  // TODO: fix this; Its part of saving/loading the level
184  //
185  _load_profile(laSystemIntegrator::getSettings()->load_player_name);
186  /*_pSheet->reset();
187  laFileParser fp_save("save/player.jrsv");
188  fp_save.readObj(_pSheet, M_FALSE);*/
189 
190  // Initialzie GUI
191  //
192  _IndicatorHP.setColor(laColor(255,255,255));
193  _IndicatorHP.setSize(512, 128);
194  _IndicatorHP.setMaxValue( ((rpgSheet_PlayableChar*)_pSheet)->properties()->nHP_Max );
195  _IndicatorHP.setTextures("gui\\HP_empty.png", "gui\\HP_full.png", 32, 512-360);
196 
197  _IndicatorXP.setColor(laColor(255,255,255));
198  _IndicatorXP.setSize(512, 128);
199  _IndicatorXP.setMaxValue( ((rpgSheet_PlayableChar*)_pSheet)->properties()->nXP_Next );
200  _IndicatorXP.setTextures("gui\\XP_empty.png", "gui\\XP_full.png", 260, 512-444);
201 
202  // Load custom sounds for the player
203  //
204  _pDieSound = laSystemIntegrator::getSound()->soundCreate("sound\\die(hearth beath).mp3");
205  _pLevelupSound = laSystemIntegrator::getSound()->soundCreate("sound\\_level_up(chant).wav");
206 
207  _pLandSound = laSystemIntegrator::getSound()->soundCreate("sound\\land(klick).WAV");
208  _pCollectSound = laSystemIntegrator::getSound()->soundCreate("sound\\collect(sonar).mp3");
209 
210  _pAttackSound = laSystemIntegrator::getSound()->soundCreate("sound\\attack(sword).mp3");
211 
212  // Initialize level-up fx
213  //
214  laFileParser fx_fp("fx\\creature_levelup.fx");
215  fxParticleSystem_params par;
216 
217  par.load(&fx_fp);
218  _cbLevelUp.create(par);
219  _cbLevelUp.enable(M_FALSE);
220 
221  // Initialize skill-up fx
222  //
223  laFileParser sk_fp("fx\\creature_skillup.fx");
224  //fxParticleSystem_params par;
225 
226  par.load(&sk_fp);
227  _cbSkillUp.create(par);
228  _cbSkillUp.enable(M_FALSE);
229 
230 
231  _outline.bTopmost = M_TRUE;
232  //_outline.id = 0x10;
233 
234  // Add the "Peaceful" weapon
235  // i.e. attacks disabled when this item is active
236  //
237  _pwPeace = new rpgWeapon();
238  _pwPeace->load( getTS()->getElement("Peaceful")->getChild("weapon") );
239 
240  rpgInventory* pInv = & ( ((rpgSheet_PlayableChar*)getSheet())->properties()->items );
241  pInv->add( _pwPeace );
242 
243  // Note: need to set this again for the player to work with ai (eg. as an auto-palyer)
244  _ai.controllers(this, this, getLevelObject()->getGlobalPercepts() );
245 
246  ERRORLEVEL_END;
247 }
248 
249 // Reflect trajectory (after touch damage is inflicted)
250 //
251 void laPlayer::reflect(/*M_BOOL bAffectX*/ const laRect2 &rReference)
252 {
253  if(!isAlive() ) return;
254  if( isImmune() ) return;
255 
256  /*int sx = _nFaceDirection;
257  int sy = M_SIGN(_ptPivot.velocity.y());
258 
259  if(bAffectX) _ptPivot.velocity[0] = - 0.5 * M_UNIT * M_PLAYER_SPEED * sx;
260  _ptPivot.velocity[1] = - 0.8 * M_UNIT * M_PLAYER_JUMP * sy;*/
261 }
262 
263 void laPlayer::attackInitiate(M_BOOL bQuick)
264 {
265  if(!isControlEnabled()) return;
266 
267  if( isPeaceful() )
268  {
269  if( _ttPeacefulAction.isElapsed() )
270  _ttPeacefulAction.reset();
271  }
272  else
273  {
274  laFightingCreature::attackInitiate(bQuick);
275  //_ttBattleStance.reset();
276  }
277 }
278 
279 void laPlayer::hit( rpgAttack attack )
280 {
281  ERRORLEVEL_BEGIN;
282 
283  // Potion-like effect, associated with the attack
284  if( attack.spPotion && (!isImmune()) )
285  {
286  // Register animator in the potion
287  //attack.spPotion.get()->pAnimatorObj = pAO;
288 
289  // Drink it
290  // NOTE: forceCollect() collects the potion automatically,
291  // otherwise it will exist in an "uncollected" state, which is not what we want
292  //
293  //collect( attack.spPotion.get() );
294 
295  if( ! attack.spPotion.get()->pAnimatorObj )
296  {
297  //MSG("new");
298  // Unlkie potion collectables, the potion-like effects of an attack don't have a level obj
299  // Bulid an object that will animate the potion
300  //
301  laCollectable * pAO = new laCollectable();
302  unsigned nTS = getTS()->getElementIndex( attack.spPotion.get()->strName );
303  //MSG( attack.spPotion.get()->strName );
304 
305  pAO->setTS( getTS() );
306  pAO->setObject( nTS, getTS()->getElement( nTS) );
307  pAO->setLevel( getLevelObject() );
308  pAO->create( getPosition() + laPoint3(0, -M_UNIT), attack.spPotion.get() );
309  pAO->forceCollect();
310 
311  attack.spPotion.get()->pAnimatorObj = pAO;
312  nestedAdd( pAO );
313  }
314  else
315  {
316  //MSG("reuse");
317 
318  //delete pAO; // There is already and AO for this type of potion
319 
320  // Strictly speaking, onIdentical() should not be called in this case
321  // but for rpgPotion this is necessary, when the potion is added as part of an attack
322  // (otherwise no way to notivy the laColelctable to reset)
323  //
324  //attack.spPotion->onIdentical();
325  attack.spPotion.get()->pAnimatorObj->resetPotion();
326  attack.spPotion.get()->pAnimatorObj->setPosition( getPosition() + laPoint3(0, -M_UNIT) );
327  }
328  }
329 
330  laFightingCreature::hit(attack);
331  ERRORLEVEL_END;
332 }
333 
334 
335 // Draw geometry
336 //
337 
338 void laPlayer::_draw_positioned(laRenderer *r) {
339  // Sommersault tweak: Compensate for the adjustment of the rotation pivot
340  if( getState()==M_STATE_SOMERSAULT ) r->transTranslate( laPoint3(0, -0.8*M_UNIT, 0) );
341 
342  //Default _draw_positioned()
343  laFightingCreature::_draw_positioned(r);
344 }
345 
346 void laPlayer::_draw_positioned_rotated(laRenderer *r) {
347  ERRORLEVEL_BEGIN;
348 
349  // Sommersault tweak: Move rotation pivot up
350  if( getState()==M_STATE_SOMERSAULT ) r->transTranslate( laPoint3(0, +0.8*M_UNIT, 0) );
351 
352  // Draw active weapon
353  //
354  rpgInventory* pInv = & ( ((rpgSheet_PlayableChar*)getSheet())->properties()->items );
355 
356  if( pInv->size() && (!isPeaceful()) )
357  {
358  rpgItem *pItem = pInv->activePtr();
359  pItem->hook.sample( getStateModel() );
360 
361  r->transPush();
362  //_offset_and_rotation(r, ptBasePos); // Standard offset and rotation for the object
363  if(_ttDeath.isEnabled()) _trans_deathfx(r); // Death Fx rotation and offset, if active
364 
365  r->transTranslate( pItem->hook.arPointSample[0] );
366  r->transRotate( M_R2D(pItem->hook.angle)-180, pItem->hook.rotationAxis() );
367 
368  r->modeOutline( _outline );
369  r->styleSet( _color /*w.pTSE->getColor("color")*/ );
370  ((laElement*) (pItem->pTSE->getParent()) )->drawGeometry( r );
371 
372  r->transPop();
373  }
374 
375  // Draw player geometry
376  laFightingCreature::_draw_positioned_rotated(r);
377 
378  ERRORLEVEL_END;
379 }
380 
381 // Draw visual FX
382 //
383 void laPlayer::drawFx(laRenderer *r, laPoint3 ptBasePos)
384 {
385  ERRORLEVEL_BEGIN;
386  if(_pNPCDialog) _pNPCDialog->draw(r, ptBasePos);
387  laFightingCreature::drawFx(r, ptBasePos);
388  ERRORLEVEL_END;
389 }
390 
391 void laPlayer::_drawFx_positioned(laRenderer *r)
392 {
393  _cbLevelUp.draw(r);
394  _cbSkillUp.draw(r);
395 
396  laFightingCreature::_drawFx_positioned(r);
397 }
398 
399 // Draw GUI associated with the player character
400 //
401 void laPlayer::drawInterface(laRenderer *r, laPoint3 ptBasePos)
402 {
403  ERRORLEVEL_BEGIN;
404  /*laSettings* ps = laSystemIntegrator::getSettings();
405  double w = ps->graphics_resolution_w;
406  double h = ps->graphics_resolution_h;*/
407 
408  _uiInventoryWindow.draw();
409  _uiPotionsWindow.draw();
410 
411  shutterHit.draw(r, ptBasePos);
412  _draw_status_ui(r, ptBasePos);
413 
414  if(_pNPCDialog)
415  _pNPCDialog->drawGUI(r, ptBasePos);
416 
417  laFightingCreature::drawInterface(r, ptBasePos);
418  shutterDeath.draw(r, ptBasePos);
419 
420  ERRORLEVEL_END;
421 }
422 
423 // State-switch mechanics
424 //
425 unsigned laPlayer::_next_state( unsigned nCurrentState )
426 {
427  // Battle stance
428  //
429  static int nOldFacing;
430 
431  if( (!_ttBattleStance.isElapsed()) && (nCurrentState==M_STATE_STAND))
432  return M_STATE_BSTAND;
433 
434  if( M_ABS(_dMonsterDistance) < M_PLAYER_ATTACK_RANGE )
435  {
436  nOldFacing = getFaceDirection();
437  setExplicitFacing(M_TRUE);
438  setFaceDirection( M_SIGN(_dMonsterDistance) );
439  if(getState()==M_STATE_WALK) return M_STATE_BWALK;
440  }
441  else if(isFacingExplict())
442  {
443  setFaceDirection(nOldFacing);
444  setExplicitFacing(M_FALSE);
445  }
446 
447  // Default state switch mechanics
448  return laFightingCreature::_next_state( nCurrentState );
449 }
450 
451 // Animate
452 //
453 void laPlayer::animate(laTimer &t)
454 {
455  ERRORLEVEL_BEGIN;
456 
457  _uiInventoryWindow.reply();
458  _uiPotionsWindow.reply();
459 
460  // Animation sub-routines
461  //
462  animate_DeathMode(t);
463  //animate__level_up(t);
464  animate_TrapDamage(t);
465  animate_BattleZoom(t);
466  animate_Attack(t);
467  animate_Movement(t);
468 
469  // Animate various triggers and FX
470  //
471  _ttPeacefulAction.animate(t);
472  _ttBattleStance.animate(t);
473  _cbLevelUp.animate(t);
474  _cbSkillUp.animate(t);
475 
476  shutterDeath.animate(t);
477  shutterHit.animate(t);
478 
479  // Any nearby monster can set the battle flag;
480  // Erase the previous state
481  _bBattleMode = M_FALSE;
482  _dMonsterDistance = 100 * M_UNIT;
483 
484  // Active NPC dialog
485  if(_pNPCDialog) _pNPCDialog->animate(t);
486 
487  laFightingCreature::animate(t);
488 
489  // Move the inventory at every frane, because it resizes dynamically
490  //
491  _uiInventoryWindow.move( laPoint3( 25, -15 ), M_AL, M_AB);
492  _uiPotionsWindow.move( laPoint3( -15, 25 ), M_AR, M_AT);
493 
494  ERRORLEVEL_END;
495 }
496 
497 // Animate death mode
498 //
499 void laPlayer::animate_DeathMode(laTimer &t)
500 {
501  // Hit effect
502  //
503  if( isAlive() && (!_ttHitImmunity.isElapsed()) && (!shutterHit.isActive()) )
504  shutterHit.activate();
505  else if( _ttHitImmunity.isElapsed() && shutterHit.isActive() )
506  shutterHit.activate(M_FALSE);
507 
508  // Respawn effect
509  //
510  if( ( !isAlive() ) && (!shutterDeath.isActive()) ) shutterDeath.activate();
511  else if( isAlive() && shutterDeath.isActive() ) shutterDeath.activate(M_FALSE);
512 
513  // Respawn
514  //
515  if( !isAlive() )
516  {
517  move(0);
518  _bBattleMode = M_TRUE;
519 
520  if( (!_ttDeath.isEnabled()) )
521  {
522  _ttDeath.reset();
523  _ttDeath.enable(M_TRUE);
524  }
525 
526  if( _ttDeath.isElapsed() )
527  getLevelObject()->reset();
528  }
529  else if( _ttDeath.isElapsed() )
530  _ttDeath.enable(M_FALSE);
531 }
532 
533 // Animate Battle Zoom
534 //
535 void laPlayer::animate_BattleZoom(laTimer &t)
536 {
537  if(_bEnableBattleZoom)
538  {
539  double current = getLevelObject()->zoom(), target = 1;
540 
541  if(_bBattleMode){
542  //Zoom in (quick)
543  if( current < 1.499 ) target = current + 1.7*t.delta();
544  else target = 1.50; // NOTE: Small difference ensures no trembling
545  }
546  else{
547  //Zoom out (slower)
548  if( current > 1.001 ) target = current - 0.4*t.delta();
549  else target = 1.0;
550  }
551 
552  getLevelObject()->zoom( target );
553  }
554 }
555 
556 // Animate trap damage
557 //
558 void laPlayer::animate_TrapDamage(laTimer &t)
559 {
560  if(!isAlive() ) return;
561 
562  if(_pActiveTrap)
563  {
564  _pActiveTrap->timer.animate(t);
565  if(_pActiveTrap->timer.isElapsed()) _pActiveTrap = NULL;
566  }
567  else if(_ptPivot.pTrap)
568  {
569  _pActiveTrap = _ptPivot.pTrap;
570 
571  /*reflect(M_FALSE);*/
572  hit( *_pActiveTrap ); //rpgAttack(M_ELEMENT_TOXIC, _pActiveTrap->nDamage)
573 
574  _pActiveTrap->timer.enable(M_TRUE);
575  _pActiveTrap->timer.reset();
576  }
577 
578  //Bottom of level trap
579  if( (_ptPivot.y()>1.1*M_UNIT*M_SEGH) ) ((rpgSheet_Fighter*)_pSheet)->properties()->nHP = 0;
580 }
581 
582 // Animate player attacks
583 //
584 void laPlayer::animate_Attack(laTimer &t)
585 {
586 }
587 
588 // Animate movement
589 //
590 void laPlayer::animate_Movement(laTimer &t)
591 {
592  static M_BOOL bOnGround=M_TRUE;
593 
594  // Detect if the player is on the ground;
595  // Play a 'tud' sound when landing
596  if( (_ptPivot.bOnGround) && (!bOnGround) )
597  {
598  _pLandSound->play();
599  }
600 
601  bOnGround = _ptPivot.bOnGround;
602 
603  // Touch damage tweak; Invalidate any non-conforming movment along X,
604  // when immunity expires
605  //
606  if( (! isImmune() ) && (_nMovementDirection != M_SIGN(_ptPivot.velocity.x())) )
607  {
608  _ptPivot.velocity.x(0);
609  }
610 }
611 
612 void laPlayer::_draw_status_ui(laRenderer *r, laPoint3 ptBasePos)
613 {
614  ERRORLEVEL_BEGIN;
615  //_nHP = getHP();
616  _IndicatorHP.value( ((rpgSheet_Fighter*)_pSheet)->properties()->nHP) ;
617  _IndicatorHP.draw(r, ptBasePos);
618 
619  _IndicatorXP.value( ((rpgSheet_PlayableChar*)_pSheet)->properties()->nXP );
620  _IndicatorXP.draw(r, ptBasePos);
621 
622  r->font("deco")->ctlAlignH(M_AC);
623  r->font("deco")->ctlSize(16);
624  r->font("deco")->ctlAlignV(M_AT);
625 
626  r->styleSet(laColor(255,117,117,255));
627  r->font("deco")->draw(ptBasePos+laPoint3(118, 86), "%d / %d",
628  ((rpgSheet_Fighter*)_pSheet)->properties()->nHP, ((rpgSheet_Fighter*)_pSheet)->properties()->nHP_Max );
629 
630  //r->styleSet(laColor(193,180,44,255));
631  //r->font("deco")->draw(ptBasePos+laPoint3(168, 86), "%d", (int)(_ModifierAttack*_nAttack));
632 
633  //r->styleSet(laColor(150,250,150,255));
634  //r->font("deco")->draw(ptBasePos+laPoint3(214, 86), "%d", (int)(_ModifierDefence*_nDefence));
635 
636  ERRORLEVEL_END;
637 }
638 
639 void laPlayer::_level_up()
640 {
641  ERRORLEVEL_BEGIN;
642  //Upgrade player properties
643  //((rpgSheet_PlayableChar*)_pSheet)->_level_up();
644 
645  //Ajust indicators
646  _IndicatorXP.setMaxValue( ((rpgSheet_PlayableChar*)_pSheet)->properties()->nXP_Next );
647  _IndicatorHP.setMaxValue( ((rpgSheet_PlayableChar*)_pSheet)->properties()->nHP_Max );
648 
649  //Level up message timing
650  fxMessages.add(1.2, laColor(255,0,255), "* Level %d *", (int)((rpgSheet_PlayableChar*)_pSheet)->properties()->nLevel);
651 
652  _pLevelupSound->play();
653 
654  _cbLevelUp.enable();
655  _cbLevelUp.reset();
656 
657  ERRORLEVEL_END;
658 }
659 
660 void laPlayer::heal( unsigned nHP )
661 {
662  ERRORLEVEL_BEGIN;
663 
664  ((rpgSheet_PlayableChar*)_pSheet)->heal( nHP );
665 
666  _pCollectSound->play();
667  fxMessages.add(1, laColor(255,0,0), "+%d HP", (int)nHP);
668 
669  ERRORLEVEL_END;
670 }
671 
672 void laPlayer::collect(unsigned nXP )
673 {
674  ERRORLEVEL_BEGIN;
675  unsigned nLevel = ((rpgSheet_PlayableChar*)_pSheet)->properties()->nLevel;
676 
677  ((rpgSheet_PlayableChar*)_pSheet)->collect( nXP );
678 
679  if( nLevel < ((rpgSheet_PlayableChar*)_pSheet)->properties()->nLevel)
680  _level_up();
681 
682  _pCollectSound->play();
683  fxMessages.add(1, laColor(255,0,255), "%d Crystals", (int)nXP );
684 
685  ERRORLEVEL_END;
686 }
687 
688 M_BOOL laPlayer::collect( rpgItem *item )
689 {
690  ERRORLEVEL_BEGIN;
691  rpgInventory* pInv = NULL;
692  laColor color = laColor(255,255,0);
693 
694  if( !strcmp(item->name().c_str(), "rpgPotion") )
695  {
696  // Put potion in the potions inventory
697  pInv = & ( ((rpgSheet_PlayableChar*)getSheet())->properties()->potions );
698  color = ((rpgPotion*) item)->color;
699  }
700  else
701  {
702  // Other itmes in the normal inventory
703  pInv = & ( ((rpgSheet_PlayableChar*)getSheet())->properties()->items );
704  }
705 
706  _pCollectSound->play();
707  fxMessages.add(1, color, "%s", item->strName);
708 
709  M_BOOL nNewEntry = pInv->add( item );
710 
711  if( strcmp(item->name().c_str(), "rpgPotion") )
712  _uiInventoryWindow.select( pInv->size()-1 );
713 
714  return nNewEntry;
715  ERRORLEVEL_END;
716 }
717 
718 void laPlayer::skillUp( unsigned nSkill )
719 {
720  ERRORLEVEL_BEGIN;
721  rpgSkillsInventory* psi = &(((rpgSheet_PlayableChar*)( getSheet() ))->properties()->skills);
722 
723  if( psi->skill(nSkill)->getCurrentIndex() + 1 < psi->skill(nSkill)->getUpgradeCount() )
724  {
725  psi->skill(nSkill)->upgrade();
726 
727  fxMessages.add(1, laColor(255,255,0), "Skill Acquired");
728  _cbSkillUp.reset();
729  _cbSkillUp.enable();
730  }
731  else
732  {
733  // TODO: play sound
734  // fxMessages.add(1, laColor(255,255,0), "Max reached");
735  }
736 
737  ERRORLEVEL_END;
738 }
laFont * font(char *strName)
Get a font renderer.
Definition: laRenderer.h:192
#define M_R2D(r)
Convert radians to degrees.
#define M_AB
Text align bottom.
#define M_UNIT
Unit of 1 meter.
Base Class for Tileset Elements.
Definition: laElement.h:48
virtual void move(laPoint3 ptNewPos, unsigned nHReference=M_AL, unsigned nVReference=M_AT)
Change window position.
Definition: uiWindow.cpp:51
#define M_AL
Text align left.
M_BOOL bTopmost
If set the outline is visible even when the object is behind something.
Definition: laRenderer.h:73
#define M_AT
Text align top.
#define M_AR
Text align right.
Animated Text Effect.
Definition: fxMessages.h:64
Potion RPG Properties.
Definition: rpgPotion.h:44
Game Settings.
Definition: laSettings.h:38
Abstract RPG Properties Sheet.
Definition: rpgSheet.h:107
2D rectangle (used by the GUI and also as a simple tool for proximity testing)
Definition: laRect2.h:39
Player RPG Properties.
#define M_ABS(a)
Return abs(a)
virtual void modeOutline(const laOutline &outline)
Set outline parameters.
Definition: laRenderer.h:174
#define M_AC
Text align center.
virtual void reply()
Handle input message.
Definition: uiInventory.cpp:93
Virtual interface for the Engine graphics renderer.
Definition: laRenderer.h:98
#define M_SIGN(a)
Return sign (+1 or -1)
Attack RPG Properties.
Definition: rpgAttack.h:55
Error handling class.
Definition: laError.h:46
File Parser.
Definition: laFileParser.h:41
Collectable Level Object.
Definition: laCollectable.h:43
virtual void draw()
Display the window.
Definition: uiInventory.cpp:55