1 /*
2 Copyright © 2011-2012 Clint Bellanger
3 Copyright © 2012 Igor Paliychuk
4 Copyright © 2012 Stefan Beller
5 Copyright © 2013 Henrik Andersson
6 Copyright © 2012-2016 Justin Jacobs
7 
8 This file is part of FLARE.
9 
10 FLARE is free software: you can redistribute it and/or modify it under the terms
11 of the GNU General Public License as published by the Free Software Foundation,
12 either version 3 of the License, or (at your option) any later version.
13 
14 FLARE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
16 PARTICULAR PURPOSE.  See the GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License along with
19 FLARE.  If not, see http://www.gnu.org/licenses/
20 */
21 
22 /**
23  * class Avatar
24  *
25  * Contains logic and rendering routines for the player avatar.
26  */
27 
28 #include "Animation.h"
29 #include "AnimationManager.h"
30 #include "AnimationSet.h"
31 #include "Avatar.h"
32 #include "CommonIncludes.h"
33 #include "CursorManager.h"
34 #include "EnemyGroupManager.h"
35 #include "Entity.h"
36 #include "EntityManager.h"
37 #include "EngineSettings.h"
38 #include "FileParser.h"
39 #include "InputState.h"
40 #include "MapRenderer.h"
41 #include "MenuActionBar.h"
42 #include "MenuExit.h"
43 #include "MenuGameOver.h"
44 #include "MenuManager.h"
45 #include "MessageEngine.h"
46 #include "ModManager.h"
47 #include "PowerManager.h"
48 #include "RenderDevice.h"
49 #include "SaveLoad.h"
50 #include "Settings.h"
51 #include "SharedGameResources.h"
52 #include "SharedResources.h"
53 #include "SoundManager.h"
54 #include "Utils.h"
55 #include "UtilsMath.h"
56 #include "UtilsParsing.h"
57 
Avatar()58 Avatar::Avatar()
59 	: Entity()
60 	, attack_cursor(false)
61 	, mm_key(settings->mouse_move_swap ? Input::MAIN2 : Input::MAIN1)
62 	, hero_stats(NULL)
63 	, charmed_stats(NULL)
64 	, act_target()
65 	, drag_walking(false)
66 	, respawn(false)
67 	, close_menus(false)
68 	, allow_movement(true)
69 	, cursor_enemy(NULL)
70 	, lock_enemy(NULL)
71 	, time_played(0)
72 	, questlog_dismissed(false)
73 	, using_main1(false)
74 	, using_main2(false)
75 	, prev_hp(0)
76 	, playing_lowhp(false)
77 	, teleport_camera_lock(false) {
78 
79 	init();
80 
81 	// load the hero's animations from hero definition file
82 	anim->increaseCount("animations/hero.txt");
83 	animationSet = anim->getAnimationSet("animations/hero.txt");
84 	activeAnimation = animationSet->getAnimation("");
85 
86 	// set cooldown_hit to duration of hit animation if undefined
87 	if (!stats.cooldown_hit_enabled) {
88 		Animation *hit_anim = animationSet->getAnimation("hit");
89 		if (hit_anim) {
90 			stats.cooldown_hit.setDuration(hit_anim->getDuration());
91 			delete hit_anim;
92 		}
93 		else {
94 			stats.cooldown_hit.setDuration(0);
95 		}
96 	}
97 
98 	loadLayerDefinitions();
99 
100 	// load foot-step definitions
101 	// @CLASS Avatar: Step sounds|Description of items/step_sounds.txt
102 	FileParser infile;
103 	if (infile.open("items/step_sounds.txt", FileParser::MOD_FILE, FileParser::ERROR_NONE)) {
104 		while (infile.next()) {
105 			if (infile.key == "id") {
106 				// @ATTR id|string|An identifier name for a set of step sounds.
107 				step_def.push_back(Step_sfx());
108 				step_def.back().id = infile.val;
109 			}
110 
111 			if (step_def.empty()) continue;
112 
113 			if (infile.key == "step") {
114 				// @ATTR step|filename|Filename of a step sound effect.
115 				step_def.back().steps.push_back(infile.val);
116 			}
117 		}
118 		infile.close();
119 	}
120 
121 	loadStepFX(stats.sfx_step);
122 }
123 
init()124 void Avatar::init() {
125 
126 	// name, base, look are set by GameStateNew so don't reset it here
127 
128 	// other init
129 	sprites = 0;
130 	stats.cur_state = StatBlock::ENTITY_STANCE;
131 	if (mapr->hero_pos_enabled) {
132 		stats.pos.x = mapr->hero_pos.x;
133 		stats.pos.y = mapr->hero_pos.y;
134 	}
135 	current_power = 0;
136 	newLevelNotification = false;
137 
138 	stats.hero = true;
139 	stats.humanoid = true;
140 	stats.level = 1;
141 	stats.xp = 0;
142 	for (size_t i = 0; i < eset->primary_stats.list.size(); ++i) {
143 		stats.primary[i] = stats.primary_starting[i] = 1;
144 		stats.primary_additional[i] = 0;
145 	}
146 	stats.speed = 0.2f;
147 	stats.recalc();
148 
149 	while (!log_msg.empty()) {
150 		log_msg.pop();
151 	}
152 	respawn = false;
153 
154 	stats.cooldown.reset(Timer::END);
155 
156 	body = -1;
157 
158 	transform_triggered = false;
159 	setPowers = false;
160 	revertPowers = false;
161 	last_transform = "";
162 
163 	power_cooldown_timers.clear();
164 	power_cast_timers.clear();
165 
166 	// Find untransform power index to use for manual untransfrom ability
167 	untransform_power = 0;
168 	std::map<PowerID, Power>::iterator power_it;
169 	for (power_it = powers->powers.begin(); power_it != powers->powers.end(); ++power_it) {
170 		if (untransform_power == 0 && power_it->second.required_items.empty() && power_it->second.spawn_type == "untransform") {
171 			untransform_power = power_it->first;
172 		}
173 
174 		power_cooldown_timers[power_it->first] = Timer();
175 		power_cast_timers[power_it->first] = Timer();
176 	}
177 }
178 
handleNewMap()179 void Avatar::handleNewMap() {
180 	cursor_enemy = NULL;
181 	lock_enemy = NULL;
182 	playing_lowhp = false;
183 }
184 
185 /**
186  * Load avatar sprite layer definitions into vector.
187  */
loadLayerDefinitions()188 void Avatar::loadLayerDefinitions() {
189 	layer_def = std::vector<std::vector<unsigned> >(8, std::vector<unsigned>());
190 	layer_reference_order = std::vector<std::string>();
191 
192 	FileParser infile;
193 	// @CLASS Avatar: Hero layers|Description of engine/hero_layers.txt
194 	if (infile.open("engine/hero_layers.txt", FileParser::MOD_FILE, FileParser::ERROR_NORMAL)) {
195 		while(infile.next()) {
196 			if (infile.key == "layer") {
197 				// @ATTR layer|direction, list(string) : Direction, Layer name(s)|Defines the hero avatar sprite layer
198 				unsigned dir = Parse::toDirection(Parse::popFirstString(infile.val));
199 				if (dir>7) {
200 					infile.error("Avatar: Hero layer direction must be in range [0,7]");
201 					Utils::logErrorDialog("Avatar: Hero layer direction must be in range [0,7]");
202 					mods->resetModConfig();
203 					Utils::Exit(1);
204 				}
205 				std::string layer = Parse::popFirstString(infile.val);
206 				while (layer != "") {
207 					// check if already in layer_reference:
208 					unsigned ref_pos;
209 					for (ref_pos = 0; ref_pos < layer_reference_order.size(); ++ref_pos)
210 						if (layer == layer_reference_order[ref_pos])
211 							break;
212 					if (ref_pos == layer_reference_order.size())
213 						layer_reference_order.push_back(layer);
214 					layer_def[dir].push_back(ref_pos);
215 
216 					layer = Parse::popFirstString(infile.val);
217 				}
218 			}
219 			else {
220 				infile.error("Avatar: '%s' is not a valid key.", infile.key.c_str());
221 			}
222 		}
223 		infile.close();
224 	}
225 
226 	// There are the positions of the items relative to layer_reference_order
227 	// so if layer_reference_order=main,body,head,off
228 	// and we got a layer=3,off,body,head,main
229 	// then the layer_def[3] looks like (3,1,2,0)
230 }
231 
loadGraphics(std::vector<Layer_gfx> _img_gfx)232 void Avatar::loadGraphics(std::vector<Layer_gfx> _img_gfx) {
233 
234 	for (unsigned int i=0; i<animsets.size(); i++) {
235 		if (animsets[i])
236 			anim->decreaseCount(animsets[i]->getName());
237 		delete anims[i];
238 	}
239 	animsets.clear();
240 	anims.clear();
241 
242 	for (unsigned int i=0; i<_img_gfx.size(); i++) {
243 		if (_img_gfx[i].gfx != "") {
244 			std::string name = "animations/avatar/"+stats.gfx_base+"/"+_img_gfx[i].gfx+".txt";
245 			anim->increaseCount(name);
246 			animsets.push_back(anim->getAnimationSet(name));
247 			animsets.back()->setParent(animationSet);
248 			anims.push_back(animsets.back()->getAnimation(activeAnimation->getName()));
249 			setAnimation("stance");
250 			if(!anims.back()->syncTo(activeAnimation)) {
251 				Utils::logError("Avatar: Error syncing animation in '%s' to 'animations/hero.txt'.", animsets.back()->getName().c_str());
252 			}
253 		}
254 		else {
255 			animsets.push_back(NULL);
256 			anims.push_back(NULL);
257 		}
258 	}
259 	anim->cleanUp();
260 }
261 
262 /**
263  * Walking/running steps sound depends on worn armor
264  */
loadStepFX(const std::string & stepname)265 void Avatar::loadStepFX(const std::string& stepname) {
266 	std::string filename = stats.sfx_step;
267 	if (stepname != "") {
268 		filename = stepname;
269 	}
270 
271 	// clear previous sounds
272 	for (unsigned i=0; i<sound_steps.size(); i++) {
273 		snd->unload(sound_steps[i]);
274 	}
275 	sound_steps.clear();
276 
277 	if (filename == "") return;
278 
279 	// A literal "NULL" means we don't want to load any new sounds
280 	// This is used when transforming, since creatures don't have step sound effects
281 	if (stepname == "NULL") return;
282 
283 	// load new sounds
284 	for (unsigned i=0; i<step_def.size(); i++) {
285 		if (step_def[i].id == filename) {
286 			sound_steps.resize(step_def[i].steps.size());
287 			for (unsigned j=0; j<sound_steps.size(); j++) {
288 				sound_steps[j] = snd->load(step_def[i].steps[j], "Avatar loading foot steps");
289 			}
290 			return;
291 		}
292 	}
293 
294 	// Could not find step sound fx
295 	Utils::logError("Avatar: Could not find footstep sounds for '%s'.", filename.c_str());
296 }
297 
298 
pressing_move()299 bool Avatar::pressing_move() {
300 	if (!allow_movement || teleport_camera_lock) {
301 		return false;
302 	}
303 	else if (stats.effects.knockback_speed != 0) {
304 		return false;
305 	}
306 	else if (settings->mouse_move) {
307 		return inpt->pressing[mm_key] && !inpt->pressing[Input::SHIFT];
308 	}
309 	else {
310 		return (inpt->pressing[Input::UP] && !inpt->lock[Input::UP]) ||
311 			   (inpt->pressing[Input::DOWN] && !inpt->lock[Input::DOWN]) ||
312 			   (inpt->pressing[Input::LEFT] && !inpt->lock[Input::LEFT]) ||
313 			   (inpt->pressing[Input::RIGHT] && !inpt->lock[Input::RIGHT]);
314 	}
315 }
316 
set_direction()317 void Avatar::set_direction() {
318 	if (teleport_camera_lock || !set_dir_timer.isEnd())
319 		return;
320 
321 	int old_dir = stats.direction;
322 
323 	// handle direction changes
324 	if (settings->mouse_move) {
325 		FPoint target = Utils::screenToMap(inpt->mouse.x, inpt->mouse.y, mapr->cam.pos.x, mapr->cam.pos.y);
326 		stats.direction = Utils::calcDirection(stats.pos.x, stats.pos.y, target.x, target.y);
327 	}
328 	else {
329 		if (inpt->pressing[Input::UP] && !inpt->lock[Input::UP] && inpt->pressing[Input::LEFT] && !inpt->lock[Input::LEFT]) stats.direction = 1;
330 		else if (inpt->pressing[Input::UP] && !inpt->lock[Input::UP] && inpt->pressing[Input::RIGHT] && !inpt->lock[Input::RIGHT]) stats.direction = 3;
331 		else if (inpt->pressing[Input::DOWN] && !inpt->lock[Input::DOWN] && inpt->pressing[Input::RIGHT] && !inpt->lock[Input::RIGHT]) stats.direction = 5;
332 		else if (inpt->pressing[Input::DOWN] && !inpt->lock[Input::DOWN] && inpt->pressing[Input::LEFT] && !inpt->lock[Input::LEFT]) stats.direction = 7;
333 		else if (inpt->pressing[Input::LEFT] && !inpt->lock[Input::LEFT]) stats.direction = 0;
334 		else if (inpt->pressing[Input::UP] && !inpt->lock[Input::UP]) stats.direction = 2;
335 		else if (inpt->pressing[Input::RIGHT] && !inpt->lock[Input::RIGHT]) stats.direction = 4;
336 		else if (inpt->pressing[Input::DOWN] && !inpt->lock[Input::DOWN]) stats.direction = 6;
337 		// Adjust for ORTHO tilesets
338 		if (eset->tileset.orientation == eset->tileset.TILESET_ORTHOGONAL &&
339 				((inpt->pressing[Input::UP] && !inpt->lock[Input::UP]) || (inpt->pressing[Input::DOWN] && !inpt->lock[Input::UP]) ||
340 				 (inpt->pressing[Input::LEFT] && !inpt->lock[Input::LEFT]) || (inpt->pressing[Input::RIGHT] && !inpt->lock[Input::RIGHT])))
341 			stats.direction = static_cast<unsigned char>((stats.direction == 7) ? 0 : stats.direction + 1);
342 	}
343 
344 	// give direction changing a 100ms cooldown
345 	// this allows the player to quickly change direction on their own without becoming overly "jittery"
346 	// the cooldown can be ended by releasing the move button, but the cooldown is so fast that it doesn't matter much (maybe a speed run tactic?)
347 	if (stats.direction != old_dir)
348 		set_dir_timer.setDuration(settings->max_frames_per_sec / 10);
349 }
350 
351 /**
352  * logic()
353  * Handle a single frame.  This includes:
354  * - move the avatar based on buttons pressed
355  * - calculate the next frame of animation
356  * - calculate camera position based on avatar position
357  */
logic()358 void Avatar::logic() {
359 	bool restrict_power_use = false;
360 	if (settings->mouse_move) {
361 		if(inpt->pressing[mm_key] && !inpt->pressing[Input::SHIFT] && !menu->act->isWithinSlots(inpt->mouse) && !menu->act->isWithinMenus(inpt->mouse)) {
362 			restrict_power_use = true;
363 		}
364 	}
365 
366 	// clear current space to allow correct movement
367 	mapr->collider.unblock(stats.pos.x, stats.pos.y);
368 
369 	// turn on all passive powers
370 	if ((stats.hp > 0 || stats.effects.triggered_death) && !respawn && !transform_triggered)
371 		powers->activatePassives(&stats);
372 
373 	if (transform_triggered)
374 		transform_triggered = false;
375 
376 	// handle when the player stops blocking
377 	if (stats.effects.triggered_block && !stats.blocking) {
378 		stats.cur_state = StatBlock::ENTITY_STANCE;
379 		stats.effects.triggered_block = false;
380 		stats.effects.clearTriggerEffects(Power::TRIGGER_BLOCK);
381 		stats.refresh_stats = true;
382 		stats.block_power = 0;
383 	}
384 
385 	stats.logic();
386 
387 	// alert on low health
388 	if (isDroppedToLowHp()) {
389 		// show message if set
390 		if (isLowHpMessageEnabled()) {
391 			logMsg(msg->get("Your health is low!"), MSG_NORMAL);
392 		}
393 		// play a sound if set in settings
394 		if (isLowHpSoundEnabled() && !playing_lowhp) {
395 			// if looping, then do not cleanup
396 			snd->play(sound_lowhp, "lowhp", snd->NO_POS, stats.sfx_lowhp_loop, !stats.sfx_lowhp_loop);
397 			playing_lowhp = true;
398 		}
399 	}
400 	// if looping, stop sounds when HP recovered above threshold
401 	if (isLowHpSoundEnabled() && !isLowHp() && playing_lowhp && stats.sfx_lowhp_loop) {
402 		snd->pauseChannel("lowhp");
403 		playing_lowhp = false;
404 	}
405 	else if (isLowHpSoundEnabled() && isLowHp() && !playing_lowhp && stats.sfx_lowhp_loop) {
406 		snd->play(sound_lowhp, "lowhp", snd->NO_POS, stats.sfx_lowhp_loop, !stats.sfx_lowhp_loop);
407 		playing_lowhp = true;
408 	}
409 	else if (!isLowHpSoundEnabled() && playing_lowhp) {
410 		snd->pauseChannel("lowhp");
411 		playing_lowhp = false;
412 	}
413 
414 	// we can not use stats.prev_hp here
415 	prev_hp = stats.hp;
416 
417 	// check level up
418 	if (stats.level < eset->xp.getMaxLevel() && stats.xp >= eset->xp.getLevelXP(stats.level + 1)) {
419 		stats.level_up = true;
420 		stats.level = eset->xp.getLevelFromXP(stats.xp);
421 		logMsg(msg->get("Congratulations, you have reached level %d!", stats.level), MSG_NORMAL);
422 		if (pc->stats.stat_points_per_level > 0) {
423 			logMsg(msg->get("You may increase one or more attributes through the Character Menu."), MSG_NORMAL);
424 			newLevelNotification = true;
425 		}
426 		if (pc->stats.power_points_per_level > 0) {
427 			logMsg(msg->get("You may unlock one or more abilities through the Powers Menu."), MSG_NORMAL);
428 		}
429 		stats.recalc();
430 		snd->play(sound_levelup, snd->DEFAULT_CHANNEL, snd->NO_POS, !snd->LOOP);
431 
432 		// if the player managed to level up while dead (e.g. via a bleeding creature), restore to life
433 		if (stats.cur_state == StatBlock::ENTITY_DEAD) {
434 			stats.cur_state = StatBlock::ENTITY_STANCE;
435 		}
436 	}
437 
438 	// assist mouse movement
439 	mm_key = settings->mouse_move_swap ? Input::MAIN2 : Input::MAIN1;
440 	if (!inpt->pressing[mm_key]) {
441 		drag_walking = false;
442 	}
443 
444 	// block some interactions when attacking/moving
445 	using_main1 = inpt->pressing[Input::MAIN1] && !inpt->lock[Input::MAIN1];
446 	using_main2 = inpt->pressing[Input::MAIN2] && !inpt->lock[Input::MAIN2];
447 
448 	// handle animation
449 	if (!stats.effects.stun) {
450 		activeAnimation->advanceFrame();
451 		for (unsigned i=0; i < anims.size(); i++) {
452 			if (anims[i] != NULL)
453 				anims[i]->advanceFrame();
454 		}
455 	}
456 
457 	// save a valid tile position in the event that we untransform on an invalid tile
458 	if (stats.transformed && mapr->collider.isValidPosition(stats.pos.x, stats.pos.y, MapCollision::MOVE_NORMAL, MapCollision::COLLIDE_HERO)) {
459 		transform_pos = stats.pos;
460 		transform_map = mapr->getFilename();
461 	}
462 
463 	PowerID mm_attack_id = (settings->mouse_move_swap ? menu->act->getSlotPower(MenuActionBar::SLOT_MAIN2) : menu->act->getSlotPower(MenuActionBar::SLOT_MAIN1));
464 	bool mm_can_use_power = true;
465 
466 	if (settings->mouse_move) {
467 		if (!inpt->pressing[mm_key]) {
468 			lock_enemy = NULL;
469 		}
470 		if (lock_enemy && lock_enemy->stats.hp <= 0) {
471 			lock_enemy = NULL;
472 		}
473 		if (mm_attack_id > 0) {
474 			if (!stats.canUsePower(mm_attack_id, !StatBlock::CAN_USE_PASSIVE)) {
475 				lock_enemy = NULL;
476 				mm_can_use_power = false;
477 			}
478 			else if (!power_cooldown_timers[mm_attack_id].isEnd()) {
479 				lock_enemy = NULL;
480 				mm_can_use_power = false;
481 			}
482 			else if (lock_enemy && powers->powers[mm_attack_id].requires_los && !mapr->collider.lineOfSight(stats.pos.x, stats.pos.y, lock_enemy->stats.pos.x, lock_enemy->stats.pos.y)) {
483 				lock_enemy = NULL;
484 				mm_can_use_power = false;
485 			}
486 		}
487 	}
488 
489 	if (teleport_camera_lock && Utils::calcDist(stats.pos, mapr->cam.pos) < 0.5f) {
490 		teleport_camera_lock = false;
491 	}
492 
493 	set_dir_timer.tick();
494 	if (!pressing_move()) {
495 		set_dir_timer.reset(Timer::END);
496 	}
497 
498 	if (!stats.effects.stun) {
499 		bool allowed_to_move;
500 		bool allowed_to_use_power = true;
501 
502 		switch(stats.cur_state) {
503 			case StatBlock::ENTITY_STANCE:
504 
505 				setAnimation("stance");
506 
507 				// allowed to move or use powers?
508 				if (settings->mouse_move) {
509 					allowed_to_move = restrict_power_use && (!inpt->lock[mm_key] || drag_walking) && !lock_enemy;
510 					allowed_to_use_power = true;
511 
512 					if ((inpt->pressing[mm_key] && inpt->pressing[Input::SHIFT]) || lock_enemy) {
513 						inpt->lock[mm_key] = false;
514 					}
515 				}
516 				else {
517 					allowed_to_move = true;
518 					allowed_to_use_power = true;
519 				}
520 
521 				// handle transitions to RUN
522 				if (allowed_to_move)
523 					set_direction();
524 
525 				if (pressing_move() && allowed_to_move) {
526 					if (move()) { // no collision
527 						if (settings->mouse_move && inpt->pressing[mm_key]) {
528 							inpt->lock[mm_key] = true;
529 							drag_walking = true;
530 						}
531 
532 						stats.cur_state = StatBlock::ENTITY_MOVE;
533 					}
534 				}
535 
536 				if (settings->mouse_move &&  settings->mouse_move_attack && cursor_enemy && !cursor_enemy->stats.hero_ally && mm_can_use_power && powers->checkCombatRange(mm_attack_id, &stats, cursor_enemy->stats.pos)) {
537 					stats.cur_state = StatBlock::ENTITY_STANCE;
538 					lock_enemy = cursor_enemy;
539 				}
540 
541 				break;
542 
543 			case StatBlock::ENTITY_MOVE:
544 
545 				setAnimation("run");
546 
547 				if (!sound_steps.empty()) {
548 					int stepfx = rand() % static_cast<int>(sound_steps.size());
549 
550 					if (activeAnimation->isFirstFrame() || activeAnimation->isActiveFrame())
551 						snd->play(sound_steps[stepfx], snd->DEFAULT_CHANNEL, snd->NO_POS, !snd->LOOP);
552 				}
553 
554 				// handle direction changes
555 				set_direction();
556 
557 				// handle transition to STANCE
558 				if (!pressing_move()) {
559 					stats.cur_state = StatBlock::ENTITY_STANCE;
560 					break;
561 				}
562 				else if (!move()) { // collide with wall
563 					stats.cur_state = StatBlock::ENTITY_STANCE;
564 					break;
565 				}
566 				else if (settings->mouse_move && inpt->pressing[Input::SHIFT]) {
567 					// when moving with the mouse, pressing Shift should stop movement and begin attacking
568 					stats.cur_state = StatBlock::ENTITY_STANCE;
569 					break;
570 				}
571 
572 				if (activeAnimation->getName() != "run")
573 					stats.cur_state = StatBlock::ENTITY_STANCE;
574 
575 				if (settings->mouse_move && settings->mouse_move_attack && cursor_enemy && !cursor_enemy->stats.hero_ally && mm_can_use_power && powers->checkCombatRange(mm_attack_id, &stats, cursor_enemy->stats.pos)) {
576 					stats.cur_state = StatBlock::ENTITY_STANCE;
577 					lock_enemy = cursor_enemy;
578 				}
579 
580 				break;
581 
582 			case StatBlock::ENTITY_POWER:
583 
584 				setAnimation(attack_anim);
585 
586 				if (attack_cursor) {
587 					curs->setCursor(CursorManager::CURSOR_ATTACK);
588 				}
589 
590 				if (activeAnimation->isFirstFrame()) {
591 					float attack_speed = (stats.effects.getAttackSpeed(attack_anim) * powers->powers[current_power].attack_speed) / 100.0f;
592 					activeAnimation->setSpeed(attack_speed);
593 					for (size_t i=0; i<anims.size(); ++i) {
594 						if (anims[i])
595 							anims[i]->setSpeed(attack_speed);
596 					}
597 					playAttackSound(attack_anim);
598 					power_cast_timers[current_power].setDuration(activeAnimation->getDuration());
599 				}
600 
601 				// do power
602 				if (activeAnimation->isActiveFrame() && !stats.hold_state) {
603 					// some powers check if the caster is blocking a tile
604 					// so we block the player tile prematurely here
605 					mapr->collider.block(stats.pos.x, stats.pos.y, !MapCollision::IS_ALLY);
606 
607 					powers->activate(current_power, &stats, act_target);
608 					power_cooldown_timers[current_power].setDuration(powers->powers[current_power].cooldown);
609 
610 					if (!stats.state_timer.isEnd())
611 						stats.hold_state = true;
612 				}
613 
614 				// animation is done, switch back to normal stance
615 				if ((activeAnimation->isLastFrame() && stats.state_timer.isEnd()) || activeAnimation->getName() != attack_anim) {
616 					stats.cur_state = StatBlock::ENTITY_STANCE;
617 					stats.cooldown.reset(Timer::BEGIN);
618 					allowed_to_use_power = false;
619 					stats.prevent_interrupt = false;
620 					if (settings->mouse_move) {
621 						drag_walking = true;
622 					}
623 				}
624 
625 				if (settings->mouse_move && lock_enemy && !powers->checkCombatRange(mm_attack_id, &stats, lock_enemy->stats.pos)) {
626 					lock_enemy = NULL;
627 				}
628 
629 				break;
630 
631 			case StatBlock::ENTITY_BLOCK:
632 
633 				setAnimation("block");
634 
635 				stats.blocking = false;
636 
637 				break;
638 
639 			case StatBlock::ENTITY_HIT:
640 
641 				setAnimation("hit");
642 
643 				if (activeAnimation->isFirstFrame()) {
644 					stats.effects.triggered_hit = true;
645 
646 					if (stats.block_power != 0) {
647 						power_cooldown_timers[stats.block_power].setDuration(powers->powers[stats.block_power].cooldown);
648 						stats.block_power = 0;
649 					}
650 				}
651 
652 				if (activeAnimation->getTimesPlayed() >= 1 || activeAnimation->getName() != "hit") {
653 					stats.cur_state = StatBlock::ENTITY_STANCE;
654 					if (settings->mouse_move) {
655 						drag_walking = true;
656 					}
657 				}
658 
659 				break;
660 
661 			case StatBlock::ENTITY_DEAD:
662 				allowed_to_use_power = false;
663 
664 				if (stats.effects.triggered_death) break;
665 
666 				if (stats.transformed) {
667 					stats.transform_duration = 0;
668 					untransform();
669 				}
670 
671 				setAnimation("die");
672 
673 				if (!stats.corpse && activeAnimation->isFirstFrame() && activeAnimation->getTimesPlayed() < 1) {
674 					stats.effects.clearEffects();
675 					stats.powers_passive.clear();
676 
677 					// reset power cooldowns
678 					std::map<size_t, Timer>::iterator pct_it;
679 					for (pct_it = power_cooldown_timers.begin(); pct_it != power_cooldown_timers.end(); ++pct_it) {
680 						pct_it->second.reset(Timer::END);
681 						power_cast_timers[pct_it->first].reset(Timer::END);
682 					}
683 
684 					// close menus in GameStatePlay
685 					close_menus = true;
686 
687 					playSound(Entity::SOUND_DIE);
688 
689 					logMsg(msg->get("You are defeated."), MSG_NORMAL);
690 
691 					if (stats.permadeath) {
692 						// ignore death penalty on permadeath and instead delete the player's saved game
693 						stats.death_penalty = false;
694 						Utils::removeSaveDir(save_load->getGameSlot());
695 						menu->exit->disableSave();
696 						menu->game_over->disableSave();
697 					}
698 					else {
699 						// raise the death penalty flag.  This is handled in MenuInventory
700 						stats.death_penalty = true;
701 					}
702 
703 					// if the player is attacking, we need to block further input
704 					if (inpt->pressing[Input::MAIN1])
705 						inpt->lock[Input::MAIN1] = true;
706 				}
707 
708 				if (!stats.corpse && (activeAnimation->getTimesPlayed() >= 1 || activeAnimation->getName() != "die")) {
709 					stats.corpse = true;
710 					menu->game_over->visible = true;
711 				}
712 
713 				// allow respawn with Accept if not permadeath
714 				if (menu->game_over->visible && menu->game_over->continue_clicked) {
715 					menu->game_over->close();
716 
717 					mapr->teleportation = true;
718 					mapr->teleport_mapname = mapr->respawn_map;
719 
720 					if (stats.permadeath) {
721 						// set these positions so it doesn't flash before jumping to Title
722 						mapr->teleport_destination.x = stats.pos.x;
723 						mapr->teleport_destination.y = stats.pos.y;
724 					}
725 					else {
726 						respawn = true;
727 
728 						// set teleportation variables.  GameEngine acts on these.
729 						mapr->teleport_destination.x = mapr->respawn_point.x;
730 						mapr->teleport_destination.y = mapr->respawn_point.y;
731 					}
732 				}
733 
734 				break;
735 
736 			default:
737 				break;
738 		}
739 
740 		// handle power usage
741 		if (allowed_to_use_power) {
742 			bool blocking = false;
743 
744 			for (unsigned i=0; i<action_queue.size(); i++) {
745 				ActionData &action = action_queue[i];
746 				PowerID power_id = powers->checkReplaceByEffect(action.power, &stats);
747 				const Power &power = powers->powers[power_id];
748 
749 				if (power.type == Power::TYPE_BLOCK)
750 					blocking = true;
751 
752 				if (power_id != 0 && (stats.cooldown.isEnd() || action.instant_item)) {
753 					FPoint target = action.target;
754 
755 					// check requirements
756 					if ((stats.cur_state == StatBlock::ENTITY_POWER || stats.cur_state == StatBlock::ENTITY_HIT) && !action.instant_item)
757 						continue;
758 					if (!stats.canUsePower(power_id, !StatBlock::CAN_USE_PASSIVE))
759 						continue;
760 					if (power.requires_los && !mapr->collider.lineOfSight(stats.pos.x, stats.pos.y, target.x, target.y))
761 						continue;
762 					if (power.requires_empty_target && !mapr->collider.isEmpty(target.x, target.y))
763 						continue;
764 					if (!power_cooldown_timers[power_id].isEnd())
765 						continue;
766 					if (!powers->hasValidTarget(power_id, &stats, target))
767 						continue;
768 
769 					// automatically target the selected enemy with melee attacks
770 					if (inpt->usingMouse() && power.type == Power::TYPE_FIXED && power.starting_pos == Power::STARTING_POS_MELEE && cursor_enemy) {
771 						target = cursor_enemy->stats.pos;
772 					}
773 
774 					// is this a power that requires changing direction?
775 					if (power.face) {
776 						stats.direction = Utils::calcDirection(stats.pos.x, stats.pos.y, target.x, target.y);
777 					}
778 
779 					if (power.new_state != Power::STATE_INSTANT) {
780 						current_power = power_id;
781 						act_target = target;
782 						attack_anim = power.attack_anim;
783 					}
784 
785 					if (power.state_duration > 0)
786 						stats.state_timer.setDuration(power.state_duration);
787 
788 					if (power.charge_speed != 0.0f)
789 						stats.charge_speed = power.charge_speed;
790 
791 					stats.prevent_interrupt = power.prevent_interrupt;
792 
793 					if (power.pre_power > 0 && Math::percentChance(power.pre_power_chance)) {
794 						powers->activate(power.pre_power, &stats, target);
795 					}
796 
797 					switch (power.new_state) {
798 						case Power::STATE_ATTACK:	// handle attack powers
799 							stats.cur_state = StatBlock::ENTITY_POWER;
800 							break;
801 
802 						case Power::STATE_INSTANT:	// handle instant powers
803 							powers->activate(power_id, &stats, target);
804 							power_cooldown_timers[power_id].setDuration(power.cooldown);
805 							break;
806 
807 						default:
808 							if (power.type == Power::TYPE_BLOCK) {
809 								stats.cur_state = StatBlock::ENTITY_BLOCK;
810 								powers->activate(power_id, &stats, target);
811 								stats.refresh_stats = true;
812 							}
813 							break;
814 					}
815 
816 					// if the player is attacking, show the attack cursor
817 					attack_cursor = (
818 						stats.cur_state == StatBlock::ENTITY_POWER &&
819 						!power.buff && !power.buff_teleport &&
820 						power.type != Power::TYPE_TRANSFORM &&
821 						power.type != Power::TYPE_BLOCK &&
822 						!(power.starting_pos == Power::STARTING_POS_SOURCE && power.speed == 0)
823 					);
824 
825 				}
826 			}
827 
828 			stats.blocking = blocking;
829 		}
830 
831 	}
832 
833 	// update camera
834 	mapr->cam.setTarget(stats.pos);
835 
836 	// check for map events
837 	mapr->checkEvents(stats.pos);
838 
839 	// decrement all cooldowns
840 	std::map<size_t, Timer>::iterator pct_it;
841 	for (pct_it = power_cooldown_timers.begin(); pct_it != power_cooldown_timers.end(); ++pct_it) {
842 		pct_it->second.tick();
843 		power_cast_timers[pct_it->first].tick();
844 	}
845 
846 	// make the current square solid
847 	mapr->collider.block(stats.pos.x, stats.pos.y, !MapCollision::IS_ALLY);
848 
849 	if (stats.state_timer.isEnd() && stats.hold_state)
850 		stats.hold_state = false;
851 
852 	if (stats.cur_state != StatBlock::ENTITY_POWER && stats.charge_speed != 0.0f)
853 		stats.charge_speed = 0.0f;
854 }
855 
transform()856 void Avatar::transform() {
857 	// dead players can't transform
858 	if (stats.hp <= 0)
859 		return;
860 
861 	// calling a transform power locks the actionbar, so we unlock it here
862 	inpt->unlockActionBar();
863 
864 	delete charmed_stats;
865 	charmed_stats = NULL;
866 
867 	Enemy_Level el = enemyg->getRandomEnemy(stats.transform_type, 0, 0);
868 
869 	if (el.type != "") {
870 		charmed_stats = new StatBlock();
871 		charmed_stats->load(el.type);
872 	}
873 	else {
874 		Utils::logError("Avatar: Could not transform into creature type '%s'", stats.transform_type.c_str());
875 		stats.transform_type = "";
876 		return;
877 	}
878 
879 	transform_triggered = true;
880 	stats.transformed = true;
881 	setPowers = true;
882 
883 	// temporary save hero stats
884 	delete hero_stats;
885 
886 	hero_stats = new StatBlock();
887 	*hero_stats = stats;
888 
889 	// do not allow two copies of the summons list
890 	hero_stats->summons.clear();
891 
892 	// replace some hero stats
893 	stats.speed = charmed_stats->speed;
894 	stats.flying = charmed_stats->flying;
895 	stats.intangible = charmed_stats->intangible;
896 	stats.humanoid = charmed_stats->humanoid;
897 	stats.animations = charmed_stats->animations;
898 	stats.powers_list = charmed_stats->powers_list;
899 	stats.powers_passive = charmed_stats->powers_passive;
900 	stats.effects.clearEffects();
901 
902 	anim->decreaseCount("animations/hero.txt");
903 	anim->increaseCount(charmed_stats->animations);
904 	animationSet = anim->getAnimationSet(charmed_stats->animations);
905 	delete activeAnimation;
906 	activeAnimation = animationSet->getAnimation("");
907 	stats.cur_state = StatBlock::ENTITY_STANCE;
908 
909 	// base stats
910 	for (int i=0; i<Stats::COUNT; ++i) {
911 		stats.starting[i] = std::max(stats.starting[i], charmed_stats->starting[i]);
912 	}
913 
914 	// resistances
915 	for (unsigned int i=0; i<stats.vulnerable.size(); i++) {
916 		stats.vulnerable[i] = std::min(stats.vulnerable[i], charmed_stats->vulnerable[i]);
917 	}
918 
919 	loadSoundsFromStatBlock(charmed_stats);
920 	loadStepFX("NULL");
921 
922 	stats.applyEffects();
923 
924 	transform_pos = stats.pos;
925 	transform_map = mapr->getFilename();
926 }
927 
untransform()928 void Avatar::untransform() {
929 	// calling a transform power locks the actionbar, so we unlock it here
930 	inpt->unlockActionBar();
931 
932 	// For timed transformations, move the player to the last valid tile when untransforming
933 	mapr->collider.unblock(stats.pos.x, stats.pos.y);
934 	if (!mapr->collider.isValidPosition(stats.pos.x, stats.pos.y, MapCollision::MOVE_NORMAL, MapCollision::COLLIDE_HERO)) {
935 		logMsg(msg->get("Transformation expired. You have been moved back to a safe place."), MSG_NORMAL);
936 		if (transform_map != mapr->getFilename()) {
937 			mapr->teleportation = true;
938 			mapr->teleport_mapname = transform_map;
939 			mapr->teleport_destination.x = floorf(transform_pos.x) + 0.5f;
940 			mapr->teleport_destination.y = floorf(transform_pos.y) + 0.5f;
941 			transform_map = "";
942 		}
943 		else {
944 			stats.pos.x = floorf(transform_pos.x) + 0.5f;
945 			stats.pos.y = floorf(transform_pos.y) + 0.5f;
946 		}
947 	}
948 	mapr->collider.block(stats.pos.x, stats.pos.y, !MapCollision::IS_ALLY);
949 
950 	stats.transformed = false;
951 	transform_triggered = true;
952 	stats.transform_type = "";
953 	revertPowers = true;
954 	stats.effects.clearEffects();
955 
956 	// revert some hero stats to last saved
957 	stats.speed = hero_stats->speed;
958 	stats.flying = hero_stats->flying;
959 	stats.intangible = hero_stats->intangible;
960 	stats.humanoid = hero_stats->humanoid;
961 	stats.animations = hero_stats->animations;
962 	stats.effects = hero_stats->effects;
963 	stats.powers_list = hero_stats->powers_list;
964 	stats.powers_passive = hero_stats->powers_passive;
965 
966 	anim->increaseCount("animations/hero.txt");
967 	anim->decreaseCount(charmed_stats->animations);
968 	animationSet = anim->getAnimationSet("animations/hero.txt");
969 	delete activeAnimation;
970 	activeAnimation = animationSet->getAnimation("");
971 	stats.cur_state = StatBlock::ENTITY_STANCE;
972 
973 	// This is a bit of a hack.
974 	// In order to switch to the stance animation, we can't already be in a stance animation
975 	setAnimation("run");
976 
977 	for (int i=0; i<Stats::COUNT; ++i) {
978 		stats.starting[i] = hero_stats->starting[i];
979 	}
980 
981 	for (unsigned int i=0; i<stats.vulnerable.size(); i++) {
982 		stats.vulnerable[i] = hero_stats->vulnerable[i];
983 	}
984 
985 	loadSounds();
986 	loadStepFX(stats.sfx_step);
987 
988 	delete charmed_stats;
989 	delete hero_stats;
990 	charmed_stats = NULL;
991 	hero_stats = NULL;
992 
993 	stats.applyEffects();
994 	stats.untransform_on_hit = false;
995 }
996 
checkTransform()997 void Avatar::checkTransform() {
998 	// handle transformation
999 	if (stats.transform_type != "" && stats.transform_type != "untransform" && stats.transformed == false)
1000 		transform();
1001 	if (stats.transform_type != "" && stats.transform_duration == 0)
1002 		untransform();
1003 }
1004 
setAnimation(std::string name)1005 void Avatar::setAnimation(std::string name) {
1006 	if (name == activeAnimation->getName())
1007 		return;
1008 
1009 	Entity::setAnimation(name);
1010 	for (unsigned i=0; i < animsets.size(); i++) {
1011 		delete anims[i];
1012 		if (animsets[i])
1013 			anims[i] = animsets[i]->getAnimation(name);
1014 		else
1015 			anims[i] = 0;
1016 	}
1017 }
1018 
resetActiveAnimation()1019 void Avatar::resetActiveAnimation() {
1020 	activeAnimation->reset(); // shield stutter
1021 	for (unsigned i=0; i < animsets.size(); i++)
1022 		if (anims[i])
1023 			anims[i]->reset();
1024 }
1025 
addRenders(std::vector<Renderable> & r)1026 void Avatar::addRenders(std::vector<Renderable> &r) {
1027 	if (!stats.transformed) {
1028 		for (unsigned i = 0; i < layer_def[stats.direction].size(); ++i) {
1029 			unsigned index = layer_def[stats.direction][i];
1030 			if (anims[index]) {
1031 				Renderable ren = anims[index]->getCurrentFrame(stats.direction);
1032 				ren.map_pos = stats.pos;
1033 				ren.prio = i+1;
1034 				stats.effects.getCurrentColor(ren.color_mod);
1035 				stats.effects.getCurrentAlpha(ren.alpha_mod);
1036 				if (stats.hp > 0) {
1037 					ren.type = Renderable::TYPE_HERO;
1038 				}
1039 				r.push_back(ren);
1040 			}
1041 		}
1042 	}
1043 	else {
1044 		Renderable ren = activeAnimation->getCurrentFrame(stats.direction);
1045 		ren.map_pos = stats.pos;
1046 		stats.effects.getCurrentColor(ren.color_mod);
1047 		stats.effects.getCurrentAlpha(ren.alpha_mod);
1048 		if (stats.hp > 0) {
1049 			ren.type = Renderable::TYPE_HERO;
1050 		}
1051 		r.push_back(ren);
1052 	}
1053 	// add effects
1054 	for (unsigned i = 0; i < stats.effects.effect_list.size(); ++i) {
1055 		if (stats.effects.effect_list[i].animation && !stats.effects.effect_list[i].animation->isCompleted()) {
1056 			Renderable ren = stats.effects.effect_list[i].animation->getCurrentFrame(0);
1057 			ren.map_pos = stats.pos;
1058 			if (stats.effects.effect_list[i].render_above) ren.prio = layer_def[stats.direction].size()+1;
1059 			else ren.prio = 0;
1060 			r.push_back(ren);
1061 		}
1062 	}
1063 }
1064 
logMsg(const std::string & str,int type)1065 void Avatar::logMsg(const std::string& str, int type) {
1066 	log_msg.push(std::pair<std::string, int>(str, type));
1067 }
1068 
1069 // isLowHp returns true if health is below set threshold
isLowHp()1070 bool Avatar::isLowHp() {
1071 	if (stats.hp == 0)
1072 		return false;
1073 	float hp_one_perc = static_cast<float>(std::max(stats.get(Stats::HP_MAX), 1)) / 100.0f;
1074 	return static_cast<float>(stats.hp)/hp_one_perc < static_cast<float>(settings->low_hp_threshold);
1075 }
1076 
1077 // isDroppedToLowHp returns true only if player hp just dropped below threshold
isDroppedToLowHp()1078 bool Avatar::isDroppedToLowHp() {
1079 	float hp_one_perc = static_cast<float>(std::max(stats.get(Stats::HP_MAX), 1)) / 100.0f;
1080 	return static_cast<float>(stats.hp)/hp_one_perc < static_cast<float>(settings->low_hp_threshold) &&
1081 		static_cast<float>(prev_hp)/hp_one_perc >= static_cast<float>(settings->low_hp_threshold);
1082 }
1083 
isLowHpMessageEnabled()1084 bool Avatar::isLowHpMessageEnabled() {
1085 	return settings->low_hp_warning_type == settings->LHP_WARN_TEXT ||
1086 		settings->low_hp_warning_type == settings->LHP_WARN_TEXT_CURSOR ||
1087 		settings->low_hp_warning_type == settings->LHP_WARN_TEXT_SOUND ||
1088 		settings->low_hp_warning_type == settings->LHP_WARN_ALL;
1089 }
1090 
isLowHpSoundEnabled()1091 bool Avatar::isLowHpSoundEnabled() {
1092 	return settings->low_hp_warning_type == settings->LHP_WARN_SOUND ||
1093 		settings->low_hp_warning_type == settings->LHP_WARN_TEXT_SOUND ||
1094 		settings->low_hp_warning_type == settings->LHP_WARN_CURSOR_SOUND ||
1095 		settings->low_hp_warning_type == settings->LHP_WARN_ALL;
1096 }
1097 
isLowHpCursorEnabled()1098 bool Avatar::isLowHpCursorEnabled() {
1099 	return settings->low_hp_warning_type == settings->LHP_WARN_CURSOR ||
1100 		settings->low_hp_warning_type == settings->LHP_WARN_TEXT_CURSOR ||
1101 		settings->low_hp_warning_type == settings->LHP_WARN_CURSOR_SOUND ||
1102 		settings->low_hp_warning_type == settings->LHP_WARN_ALL;
1103 }
1104 
~Avatar()1105 Avatar::~Avatar() {
1106 	if (stats.transformed && charmed_stats && charmed_stats->animations != "") {
1107 		anim->decreaseCount(charmed_stats->animations);
1108 	}
1109 	else {
1110 		anim->decreaseCount("animations/hero.txt");
1111 	}
1112 
1113 	for (unsigned int i=0; i<animsets.size(); i++) {
1114 		if (animsets[i])
1115 			anim->decreaseCount(animsets[i]->getName());
1116 		delete anims[i];
1117 	}
1118 	anim->cleanUp();
1119 
1120 	delete charmed_stats;
1121 	delete hero_stats;
1122 
1123 	unloadSounds();
1124 
1125 	for (unsigned i=0; i<sound_steps.size(); i++)
1126 		snd->unload(sound_steps[i]);
1127 }
1128