/src/Avatar.cpp

http://github.com/clintbellanger/flare · C++ · 708 lines · 480 code · 125 blank · 103 comment · 218 complexity · e189af7a96010404e101275fcaa49e44 MD5 · raw file

  1. /*
  2. Copyright 2011 Clint Bellanger
  3. This file is part of FLARE.
  4. FLARE is free software: you can redistribute it and/or modify it under the terms
  5. of the GNU General Public License as published by the Free Software Foundation,
  6. either version 3 of the License, or (at your option) any later version.
  7. FLARE is distributed in the hope that it will be useful, but WITHOUT ANY
  8. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  9. PARTICULAR PURPOSE. See the GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License along with
  11. FLARE. If not, see http://www.gnu.org/licenses/
  12. */
  13. /**
  14. * class Avatar
  15. *
  16. * Contains logic and rendering routines for the player avatar.
  17. */
  18. #include "Avatar.h"
  19. #include "SharedResources.h"
  20. #include <sstream>
  21. using namespace std;
  22. Avatar::Avatar(PowerManager *_powers, MapIso *_map) : Entity(_map), powers(_powers) {
  23. init();
  24. // default hero animation data
  25. stats.cooldown = 4;
  26. // load the hero's animations from hero definition file
  27. loadAnimations("animations/hero.txt");
  28. }
  29. void Avatar::init() {
  30. // name, base, look are set by GameStateNew so don't reset it here
  31. // other init
  32. sprites = 0;
  33. stats.cur_state = AVATAR_STANCE;
  34. stats.pos.x = map->spawn.x;
  35. stats.pos.y = map->spawn.y;
  36. stats.direction = map->spawn_dir;
  37. current_power = -1;
  38. newLevelNotification = false;
  39. lockSwing = false;
  40. lockCast = false;
  41. lockShoot = false;
  42. stats.hero = true;
  43. stats.level = 1;
  44. stats.xp = 0;
  45. stats.physical_character = 1;
  46. stats.mental_character = 1;
  47. stats.offense_character = 1;
  48. stats.defense_character = 1;
  49. stats.physical_additional = 0;
  50. stats.mental_additional = 0;
  51. stats.offense_additional = 0;
  52. stats.defense_additional = 0;
  53. stats.speed = 14;
  54. stats.dspeed = 10;
  55. stats.recalc();
  56. log_msg = "";
  57. stats.cooldown_ticks = 0;
  58. haz = NULL;
  59. img_main = "";
  60. img_armor = "";
  61. img_off = "";
  62. for (int i = 0; i < POWER_COUNT; i++) {
  63. stats.hero_cooldown[i] = 0;
  64. }
  65. for (int i=0; i<4; i++) {
  66. sound_steps[i] = NULL;
  67. }
  68. }
  69. void Avatar::loadGraphics(const string& _img_main, string _img_armor, const string& _img_off) {
  70. SDL_Surface *gfx_main = NULL;
  71. SDL_Surface *gfx_off = NULL;
  72. SDL_Surface *gfx_head = NULL;
  73. SDL_Rect src;
  74. SDL_Rect dest;
  75. // Default appearance
  76. if (_img_armor == "") _img_armor = "clothes";
  77. // Check if we really need to change the graphics
  78. if (_img_main != img_main || _img_armor != img_armor || _img_off != img_off) {
  79. img_main = _img_main;
  80. img_armor = _img_armor;
  81. img_off = _img_off;
  82. // composite the hero graphic
  83. if (sprites) SDL_FreeSurface(sprites);
  84. sprites = IMG_Load(mods->locate("images/avatar/" + stats.base + "/" + img_armor + ".png").c_str());
  85. if (img_main != "") gfx_main = IMG_Load(mods->locate("images/avatar/" + stats.base + "/" + img_main + ".png").c_str());
  86. if (img_off != "") gfx_off = IMG_Load(mods->locate("images/avatar/" + stats.base + "/" + img_off + ".png").c_str());
  87. gfx_head = IMG_Load(mods->locate("images/avatar/" + stats.base + "/" + stats.head + ".png").c_str());
  88. SDL_SetColorKey( sprites, SDL_SRCCOLORKEY, SDL_MapRGB(sprites->format, 255, 0, 255) );
  89. if (gfx_main) SDL_SetColorKey( gfx_main, SDL_SRCCOLORKEY, SDL_MapRGB(gfx_main->format, 255, 0, 255) );
  90. if (gfx_off) SDL_SetColorKey( gfx_off, SDL_SRCCOLORKEY, SDL_MapRGB(gfx_off->format, 255, 0, 255) );
  91. if (gfx_head) SDL_SetColorKey( gfx_head, SDL_SRCCOLORKEY, SDL_MapRGB(gfx_head->format, 255, 0, 255) );
  92. // assuming the hero is right-handed, we know the layer z-order
  93. // copy the furthest hand first
  94. src.w = dest.w = 4096;
  95. src.h = dest.h = 256;
  96. src.x = dest.x = 0;
  97. src.y = dest.y = 0;
  98. if (gfx_main) SDL_BlitSurface(gfx_main, &src, sprites, &dest); // row 0,1 main hand
  99. src.y = dest.y = 768;
  100. if (gfx_main) SDL_BlitSurface(gfx_main, &src, sprites, &dest); // row 6,7 main hand
  101. src.h = dest.h = 512;
  102. src.y = dest.y = 256;
  103. if (gfx_off) SDL_BlitSurface(gfx_off, &src, sprites, &dest); // row 2-5 off hand
  104. // copy the head in the middle
  105. src.h = dest.h = 1024;
  106. src.y = dest.y = 0;
  107. if (gfx_head) SDL_BlitSurface(gfx_head, &src, sprites, &dest); // head
  108. // copy the closest hand last
  109. src.w = dest.w = 4096;
  110. src.h = dest.h = 256;
  111. src.x = dest.x = 0;
  112. src.y = dest.y = 0;
  113. if (gfx_off) SDL_BlitSurface(gfx_off, &src, sprites, &dest); // row 0,1 off hand
  114. src.y = dest.y = 768;
  115. if (gfx_off) SDL_BlitSurface(gfx_off, &src, sprites, &dest); // row 6,7 off hand
  116. src.h = dest.h = 512;
  117. src.y = dest.y = 256;
  118. if (gfx_main) SDL_BlitSurface(gfx_main, &src, sprites, &dest); // row 2-5 main hand
  119. if (gfx_main) SDL_FreeSurface(gfx_main);
  120. if (gfx_off) SDL_FreeSurface(gfx_off);
  121. if (gfx_head) SDL_FreeSurface(gfx_head);
  122. // optimize
  123. SDL_Surface *cleanup = sprites;
  124. sprites = SDL_DisplayFormatAlpha(sprites);
  125. SDL_FreeSurface(cleanup);
  126. }
  127. }
  128. void Avatar::loadSounds() {
  129. sound_melee = Mix_LoadWAV(mods->locate("soundfx/melee_attack.ogg").c_str());
  130. sound_hit = Mix_LoadWAV(mods->locate("soundfx/" + stats.base + "_hit.ogg").c_str());
  131. sound_die = Mix_LoadWAV(mods->locate("soundfx/" + stats.base + "_die.ogg").c_str());
  132. sound_block = Mix_LoadWAV(mods->locate("soundfx/powers/block.ogg").c_str());
  133. level_up = Mix_LoadWAV(mods->locate("soundfx/level_up.ogg").c_str());
  134. if (!sound_melee || !sound_hit || !sound_die || !level_up) {
  135. printf("Mix_LoadWAV: %s\n", Mix_GetError());
  136. }
  137. }
  138. /**
  139. * Walking/running steps sound depends on worn armor
  140. */
  141. void Avatar::loadStepFX(const string& stepname) {
  142. // TODO: put default step sound in engine config file
  143. string filename = "cloth";
  144. if (stepname != "") {
  145. filename = stepname;
  146. }
  147. // clear previous sounds
  148. for (int i=0; i<4; i++) {
  149. if (sound_steps[i] != NULL) {
  150. Mix_FreeChunk(sound_steps[i]);
  151. sound_steps[i] = NULL;
  152. }
  153. }
  154. // load new sounds
  155. sound_steps[0] = Mix_LoadWAV(mods->locate("soundfx/steps/step_" + filename + "1.ogg").c_str());
  156. sound_steps[1] = Mix_LoadWAV(mods->locate("soundfx/steps/step_" + filename + "2.ogg").c_str());
  157. sound_steps[2] = Mix_LoadWAV(mods->locate("soundfx/steps/step_" + filename + "3.ogg").c_str());
  158. sound_steps[3] = Mix_LoadWAV(mods->locate("soundfx/steps/step_" + filename + "4.ogg").c_str());
  159. }
  160. bool Avatar::pressing_move() {
  161. if(MOUSE_MOVE) {
  162. return inp->pressing[MAIN1];
  163. } else {
  164. return inp->pressing[UP] || inp->pressing[DOWN] || inp->pressing[LEFT] || inp->pressing[RIGHT];
  165. }
  166. }
  167. void Avatar::set_direction() {
  168. // handle direction changes
  169. if(MOUSE_MOVE) {
  170. Point target = screen_to_map(inp->mouse.x, inp->mouse.y, stats.pos.x, stats.pos.y);
  171. // if no line of movement to target, use pathfinder
  172. if( !map->collider.line_of_movement(stats.pos.x, stats.pos.y, target.x, target.y) ) {
  173. vector<Point> path;
  174. // if a path is returned, target first waypoint
  175. if ( map->collider.compute_path(stats.pos,target,path,1000) ) {
  176. target = path.back();
  177. }
  178. }
  179. stats.direction = face(target.x, target.y);
  180. } else {
  181. if(inp->pressing[UP] && inp->pressing[LEFT]) stats.direction = 1;
  182. else if(inp->pressing[UP] && inp->pressing[RIGHT]) stats.direction = 3;
  183. else if(inp->pressing[DOWN] && inp->pressing[RIGHT]) stats.direction = 5;
  184. else if(inp->pressing[DOWN] && inp->pressing[LEFT]) stats.direction = 7;
  185. else if(inp->pressing[LEFT]) stats.direction = 0;
  186. else if(inp->pressing[UP]) stats.direction = 2;
  187. else if(inp->pressing[RIGHT]) stats.direction = 4;
  188. else if(inp->pressing[DOWN]) stats.direction = 6;
  189. }
  190. }
  191. /**
  192. * logic()
  193. * Handle a single frame. This includes:
  194. * - move the avatar based on buttons pressed
  195. * - calculate the next frame of animation
  196. * - calculate camera position based on avatar position
  197. *
  198. * @param power_index The actionbar power activated. -1 means no power.
  199. */
  200. void Avatar::logic(int actionbar_power, bool restrictPowerUse) {
  201. Point target;
  202. int stepfx;
  203. stats.logic();
  204. if (stats.forced_move_duration > 0) {
  205. move();
  206. // calc new cam position from player position
  207. // cam is focused at player position
  208. map->cam.x = stats.pos.x;
  209. map->cam.y = stats.pos.y;
  210. map->hero_tile.x = stats.pos.x / 32;
  211. map->hero_tile.y = stats.pos.y / 32;
  212. return;
  213. }
  214. if (stats.stun_duration > 0) return;
  215. bool allowed_to_move;
  216. bool allowed_to_use_power;
  217. // check level up
  218. int max_spendable_stat_points = 16;
  219. if (stats.xp >= stats.xp_table[stats.level] && stats.level < MAX_CHARACTER_LEVEL) {
  220. stats.level++;
  221. stringstream ss;
  222. ss << msg->get("Congratulations, you have reached level %d!", stats.level);
  223. if (stats.level < max_spendable_stat_points) {
  224. ss << " " << msg->get("You may increase one attribute through the Character Menu.");
  225. newLevelNotification = true;
  226. }
  227. log_msg = ss.str();
  228. stats.recalc();
  229. Mix_PlayChannel(-1, level_up, 0);
  230. }
  231. // check for bleeding spurt
  232. if (stats.bleed_duration % 30 == 1) {
  233. powers->activate(POWER_SPARK_BLOOD, &stats, stats.pos);
  234. }
  235. // check for bleeding to death
  236. if (stats.hp == 0 && !(stats.cur_state == AVATAR_DEAD)) {
  237. stats.cur_state = AVATAR_DEAD;
  238. }
  239. // assist mouse movement
  240. if (!inp->pressing[MAIN1]) drag_walking = false;
  241. // handle animation
  242. activeAnimation->advanceFrame();
  243. switch(stats.cur_state) {
  244. case AVATAR_STANCE:
  245. setAnimation("stance");
  246. // allowed to move or use powers?
  247. if (MOUSE_MOVE) {
  248. allowed_to_move = restrictPowerUse && (!inp->lock[MAIN1] || drag_walking);
  249. allowed_to_use_power = !allowed_to_move;
  250. }
  251. else {
  252. allowed_to_move = true;
  253. allowed_to_use_power = true;
  254. }
  255. // handle transitions to RUN
  256. if (allowed_to_move)
  257. set_direction();
  258. if (pressing_move() && allowed_to_move) {
  259. if (MOUSE_MOVE && inp->pressing[MAIN1]) {
  260. inp->lock[MAIN1] = true;
  261. drag_walking = true;
  262. }
  263. if (move()) { // no collision
  264. stats.cur_state = AVATAR_RUN;
  265. }
  266. }
  267. // handle power usage
  268. if (allowed_to_use_power && actionbar_power != -1 && stats.cooldown_ticks == 0) {
  269. target = screen_to_map(inp->mouse.x, inp->mouse.y + powers->powers[actionbar_power].aim_assist, stats.pos.x, stats.pos.y);
  270. // check requirements
  271. if (powers->powers[actionbar_power].requires_mp > stats.mp)
  272. break;
  273. if (powers->powers[actionbar_power].requires_physical_weapon && !stats.wielding_physical)
  274. break;
  275. if (powers->powers[actionbar_power].requires_mental_weapon && !stats.wielding_mental)
  276. break;
  277. if (powers->powers[actionbar_power].requires_offense_weapon && !stats.wielding_offense)
  278. break;
  279. if (powers->powers[actionbar_power].requires_los && !map->collider.line_of_sight(stats.pos.x, stats.pos.y, target.x, target.y))
  280. break;
  281. if (powers->powers[actionbar_power].requires_empty_target && !map->collider.is_empty(target.x, target.y))
  282. break;
  283. if (stats.hero_cooldown[actionbar_power] > 0)
  284. break;
  285. stats.hero_cooldown[actionbar_power] = powers->powers[actionbar_power].cooldown; //set the cooldown timer
  286. current_power = actionbar_power;
  287. act_target.x = target.x;
  288. act_target.y = target.y;
  289. // is this a power that requires changing direction?
  290. if (powers->powers[current_power].face) {
  291. stats.direction = face(target.x, target.y);
  292. }
  293. // handle melee powers
  294. if (powers->powers[current_power].new_state == POWSTATE_SWING) {
  295. stats.cur_state = AVATAR_MELEE;
  296. break;
  297. }
  298. // handle ranged powers
  299. if (powers->powers[current_power].new_state == POWSTATE_SHOOT) {
  300. stats.cur_state = AVATAR_SHOOT;
  301. break;
  302. }
  303. // handle ment powers
  304. if (powers->powers[current_power].new_state == POWSTATE_CAST) {
  305. stats.cur_state = AVATAR_CAST;
  306. break;
  307. }
  308. if (powers->powers[current_power].new_state == POWSTATE_BLOCK) {
  309. stats.cur_state = AVATAR_BLOCK;
  310. stats.blocking = true;
  311. break;
  312. }
  313. }
  314. break;
  315. case AVATAR_RUN:
  316. setAnimation("run");
  317. stepfx = rand() % 4;
  318. if (activeAnimation->getCurFrame() == 1 || activeAnimation->getCurFrame() == activeAnimation->getMaxFrame()/2) {
  319. Mix_PlayChannel(-1, sound_steps[stepfx], 0);
  320. }
  321. // allowed to move or use powers?
  322. if (MOUSE_MOVE) {
  323. allowed_to_use_power = !(restrictPowerUse && !inp->lock[MAIN1]);
  324. }
  325. else {
  326. allowed_to_use_power = true;
  327. }
  328. // handle direction changes
  329. set_direction();
  330. // handle transition to STANCE
  331. if (!pressing_move()) {
  332. stats.cur_state = AVATAR_STANCE;
  333. break;
  334. }
  335. else if (!move()) { // collide with wall
  336. stats.cur_state = AVATAR_STANCE;
  337. break;
  338. }
  339. // handle power usage
  340. if (allowed_to_use_power && actionbar_power != -1 && stats.cooldown_ticks == 0) {
  341. target = screen_to_map(inp->mouse.x, inp->mouse.y + powers->powers[actionbar_power].aim_assist, stats.pos.x, stats.pos.y);
  342. // check requirements
  343. if (powers->powers[actionbar_power].requires_mp > stats.mp)
  344. break;
  345. if (powers->powers[actionbar_power].requires_physical_weapon && !stats.wielding_physical)
  346. break;
  347. if (powers->powers[actionbar_power].requires_mental_weapon && !stats.wielding_mental)
  348. break;
  349. if (powers->powers[actionbar_power].requires_offense_weapon && !stats.wielding_offense)
  350. break;
  351. if (powers->powers[actionbar_power].requires_los && !map->collider.line_of_sight(stats.pos.x, stats.pos.y, target.x, target.y))
  352. break;
  353. if (powers->powers[actionbar_power].requires_empty_target && !map->collider.is_empty(target.x, target.y))
  354. break;
  355. if (stats.hero_cooldown[actionbar_power] > 0)
  356. break;
  357. stats.hero_cooldown[actionbar_power] = powers->powers[actionbar_power].cooldown; //set the cooldown timer
  358. current_power = actionbar_power;
  359. act_target.x = target.x;
  360. act_target.y = target.y;
  361. // is this a power that requires changing direction?
  362. if (powers->powers[current_power].face) {
  363. stats.direction = face(target.x, target.y);
  364. }
  365. // handle melee powers
  366. if (powers->powers[current_power].new_state == POWSTATE_SWING) {
  367. stats.cur_state = AVATAR_MELEE;
  368. break;
  369. }
  370. // handle ranged powers
  371. if (powers->powers[current_power].new_state == POWSTATE_SHOOT) {
  372. stats.cur_state = AVATAR_SHOOT;
  373. break;
  374. }
  375. // handle ment powers
  376. if (powers->powers[current_power].new_state == POWSTATE_CAST) {
  377. stats.cur_state = AVATAR_CAST;
  378. break;
  379. }
  380. if (powers->powers[current_power].new_state == POWSTATE_BLOCK) {
  381. stats.cur_state = AVATAR_BLOCK;
  382. stats.blocking = true;
  383. break;
  384. }
  385. }
  386. break;
  387. case AVATAR_MELEE:
  388. setAnimation("melee");
  389. if (activeAnimation->getCurFrame() == 1) {
  390. Mix_PlayChannel(-1, sound_melee, 0);
  391. }
  392. // do power
  393. if (activeAnimation->getCurFrame() == activeAnimation->getMaxFrame()/2) {
  394. powers->activate(current_power, &stats, act_target);
  395. }
  396. if (activeAnimation->getTimesPlayed() >= 1) {
  397. stats.cur_state = AVATAR_STANCE;
  398. if (stats.haste_duration == 0) stats.cooldown_ticks += stats.cooldown;
  399. }
  400. break;
  401. case AVATAR_CAST:
  402. setAnimation("ment");
  403. // do power
  404. if (activeAnimation->getCurFrame() == activeAnimation->getMaxFrame()/2) {
  405. powers->activate(current_power, &stats, act_target);
  406. }
  407. if (activeAnimation->getTimesPlayed() >= 1) {
  408. stats.cur_state = AVATAR_STANCE;
  409. if (stats.haste_duration == 0) stats.cooldown_ticks += stats.cooldown;
  410. }
  411. break;
  412. case AVATAR_SHOOT:
  413. setAnimation("ranged");
  414. // do power
  415. if (activeAnimation->getCurFrame() == activeAnimation->getMaxFrame()/2) {
  416. powers->activate(current_power, &stats, act_target);
  417. }
  418. if (activeAnimation->getTimesPlayed() >= 1) {
  419. stats.cur_state = AVATAR_STANCE;
  420. if (stats.haste_duration == 0) stats.cooldown_ticks += stats.cooldown;
  421. }
  422. break;
  423. case AVATAR_BLOCK:
  424. setAnimation("block");
  425. if (powers->powers[actionbar_power].new_state != POWSTATE_BLOCK) {
  426. stats.cur_state = AVATAR_STANCE;
  427. stats.blocking = false;
  428. }
  429. break;
  430. case AVATAR_HIT:
  431. setAnimation("hit");
  432. if (activeAnimation->getTimesPlayed() >= 1) {
  433. stats.cur_state = AVATAR_STANCE;
  434. }
  435. break;
  436. case AVATAR_DEAD:
  437. setAnimation("die");
  438. if (activeAnimation->getCurFrame() == 1 && activeAnimation->getTimesPlayed() < 1) {
  439. Mix_PlayChannel(-1, sound_die, 0);
  440. log_msg = msg->get("You are defeated. You lose half your gold. Press Enter to continue.");
  441. }
  442. if (activeAnimation->getTimesPlayed() >= 1) {
  443. stats.corpse = true;
  444. }
  445. // allow respawn with Accept
  446. if (inp->pressing[ACCEPT]) {
  447. stats.hp = stats.maxhp;
  448. stats.mp = stats.maxmp;
  449. stats.alive = true;
  450. stats.corpse = false;
  451. stats.cur_state = AVATAR_STANCE;
  452. // remove temporary effects
  453. stats.clearEffects();
  454. // set teleportation variables. GameEngine acts on these.
  455. map->teleportation = true;
  456. map->teleport_mapname = map->respawn_map;
  457. map->teleport_destination.x = map->respawn_point.x;
  458. map->teleport_destination.y = map->respawn_point.y;
  459. }
  460. break;
  461. default:
  462. break;
  463. }
  464. // calc new cam position from player position
  465. // cam is focused at player position
  466. map->cam.x = stats.pos.x;
  467. map->cam.y = stats.pos.y;
  468. map->hero_tile.x = stats.pos.x / 32;
  469. map->hero_tile.y = stats.pos.y / 32;
  470. // check for map events
  471. map->checkEvents(stats.pos);
  472. // decrement all cooldowns
  473. for (int i = 0; i < POWER_COUNT; i++){
  474. stats.hero_cooldown[i] -= 1000 / FRAMES_PER_SEC;
  475. if (stats.hero_cooldown[i] < 0) stats.hero_cooldown[i] = 0;
  476. }
  477. }
  478. /**
  479. * Called by HazardManager
  480. * Return false on a miss
  481. */
  482. bool Avatar::takeHit(Hazard h) {
  483. if (stats.cur_state != AVATAR_DEAD) {
  484. // check miss
  485. int avoidance = stats.avoidance;
  486. if (stats.blocking) avoidance *= 2;
  487. if (rand() % 100 > (h.accuracy - avoidance + 25)) return false;
  488. int dmg;
  489. if (h.dmg_min == h.dmg_max) dmg = h.dmg_min;
  490. else dmg = h.dmg_min + (rand() % (h.dmg_max - h.dmg_min + 1));
  491. // apply elemental resistance
  492. // TODO: make this generic
  493. if (h.trait_elemental == ELEMENT_FIRE) {
  494. dmg = (dmg * stats.attunement_fire) / 100;
  495. }
  496. if (h.trait_elemental == ELEMENT_WATER) {
  497. dmg = (dmg * stats.attunement_ice) / 100;
  498. }
  499. // apply absorption
  500. int absorption;
  501. if (!h.trait_armor_penetration) { // armor penetration ignores all absorption
  502. if (stats.absorb_min == stats.absorb_max) absorption = stats.absorb_min;
  503. else absorption = stats.absorb_min + (rand() % (stats.absorb_max - stats.absorb_min + 1));
  504. if (stats.blocking) absorption += absorption + stats.absorb_max; // blocking doubles your absorb amount
  505. dmg = dmg - absorption;
  506. if (dmg < 1 && !stats.blocking) dmg = 1; // when blocking, dmg can be reduced to 0
  507. if (dmg <= 0) {
  508. dmg = 0;
  509. Mix_PlayChannel(-1, sound_block, 0);
  510. activeAnimation->reset(); // shield stutter
  511. }
  512. }
  513. int prev_hp = stats.hp;
  514. stats.takeDamage(dmg);
  515. // after effects
  516. if (stats.hp > 0 && stats.immunity_duration == 0 && dmg > 0) {
  517. if (h.stun_duration > stats.stun_duration) stats.stun_duration = h.stun_duration;
  518. if (h.slow_duration > stats.slow_duration) stats.slow_duration = h.slow_duration;
  519. if (h.bleed_duration > stats.bleed_duration) stats.bleed_duration = h.bleed_duration;
  520. if (h.immobilize_duration > stats.immobilize_duration) stats.immobilize_duration = h.immobilize_duration;
  521. if (h.forced_move_duration > stats.forced_move_duration) stats.forced_move_duration = h.forced_move_duration;
  522. if (h.forced_move_speed != 0) {
  523. float theta = powers->calcTheta(h.src_stats->pos.x, h.src_stats->pos.y, stats.pos.x, stats.pos.y);
  524. stats.forced_speed.x = ceil((float)h.forced_move_speed * cos(theta));
  525. stats.forced_speed.y = ceil((float)h.forced_move_speed * sin(theta));
  526. }
  527. if (h.hp_steal != 0) {
  528. h.src_stats->hp += (int)ceil((float)dmg * (float)h.hp_steal / 100.0);
  529. if (h.src_stats->hp > h.src_stats->maxhp) h.src_stats->hp = h.src_stats->maxhp;
  530. }
  531. // if (h.mp_steal != 0) { //enemies don't have MP
  532. }
  533. // post effect power
  534. if (h.post_power >= 0 && dmg > 0) {
  535. powers->activate(h.post_power, h.src_stats, stats.pos);
  536. }
  537. // Power-specific: Vengeance gains stacks when blocking
  538. if (stats.blocking && stats.physdef >= 9) {
  539. if (stats.vengeance_stacks < 3)
  540. stats.vengeance_stacks++;
  541. }
  542. if (stats.hp <= 0) {
  543. stats.cur_state = AVATAR_DEAD;
  544. // raise the death penalty flag. Another module will read this and reset.
  545. stats.death_penalty = true;
  546. }
  547. else if (prev_hp > stats.hp) { // only interrupt if damage was taken
  548. Mix_PlayChannel(-1, sound_hit, 0);
  549. stats.cur_state = AVATAR_HIT;
  550. }
  551. return true;
  552. }
  553. return false;
  554. }
  555. /**
  556. * getRender()
  557. * Map objects need to be drawn in Z order, so we allow a parent object (GameEngine)
  558. * to collect all mobile sprites each frame.
  559. */
  560. Renderable Avatar::getRender() {
  561. Renderable r = activeAnimation->getCurrentFrame(stats.direction);
  562. r.sprite = sprites;
  563. r.map_pos.x = stats.pos.x;
  564. r.map_pos.y = stats.pos.y;
  565. return r;
  566. }
  567. Avatar::~Avatar() {
  568. SDL_FreeSurface(sprites);
  569. Mix_FreeChunk(sound_melee);
  570. Mix_FreeChunk(sound_hit);
  571. Mix_FreeChunk(sound_die);
  572. Mix_FreeChunk(sound_block);
  573. Mix_FreeChunk(sound_steps[0]);
  574. Mix_FreeChunk(sound_steps[1]);
  575. Mix_FreeChunk(sound_steps[2]);
  576. Mix_FreeChunk(sound_steps[3]);
  577. Mix_FreeChunk(level_up);
  578. delete haz;
  579. }