Example #1
0
/**
 * logic()
 * Handle a single frame.  This includes:
 * - move the avatar based on buttons pressed
 * - calculate the next frame of animation
 * - calculate camera position based on avatar position
 *
 * @param power_index The actionbar power activated.  -1 means no power.
 */
void Avatar::logic(int actionbar_power, bool restrictPowerUse) {

	Point target;
	int stepfx;
	stats.logic();
	if (stats.stun_duration > 0) return;
	bool allowed_to_move;
	bool allowed_to_use_power;
	
	// check level up
	if (stats.level < 17 && stats.xp >= stats.xp_table[stats.level]) {
		stats.level++;
		stringstream ss;
		ss << "Congratulations, you have reached level " << stats.level << "! You may increase one attribute through the Character Menu.";
		log_msg = ss.str();
		stats.recalc();
		Mix_PlayChannel(-1, level_up, 0);
	}

	// check for bleeding spurt
	if (stats.bleed_duration % 30 == 1) {
		powers->activate(POWER_SPARK_BLOOD, &stats, stats.pos);
	}
	// check for bleeding to death
	if (stats.hp == 0 && !(stats.cur_state == AVATAR_DEAD)) {
		stats.cur_state = AVATAR_DEAD;
	}		
	
	// assist mouse movement
	if (!inp->pressing[MAIN1]) drag_walking = false;
	
	// handle animation
	activeAnimation->advanceFrame();
			
	switch(stats.cur_state) {
		case AVATAR_STANCE:

			setAnimation("stance");
		
			// allowed to move or use powers?
			if (MOUSE_MOVE) {
				allowed_to_move = restrictPowerUse && (!inp->lock[MAIN1] || drag_walking);
				allowed_to_use_power = !allowed_to_move;
			}
			else {
				allowed_to_move = true;
				allowed_to_use_power = true;
			}

			// handle transitions to RUN
			if (allowed_to_move)
				set_direction();
			
			if (pressing_move() && allowed_to_move) {
				if (MOUSE_MOVE && inp->pressing[MAIN1]) {
					inp->lock[MAIN1] = true;
					drag_walking = true;
				}
				
				if (move()) { // no collision
					stats.cur_state = AVATAR_RUN;
				}

			}
			// handle power usage
			if (allowed_to_use_power && actionbar_power != -1 && stats.cooldown_ticks == 0) {				
				target = screen_to_map(inp->mouse.x,  inp->mouse.y + powers->powers[actionbar_power].aim_assist, stats.pos.x, stats.pos.y);
			
				// check requirements
				if (powers->powers[actionbar_power].requires_mp > stats.mp)
					break;
				if (powers->powers[actionbar_power].requires_physical_weapon && !stats.wielding_physical)
					break;
				if (powers->powers[actionbar_power].requires_mental_weapon && !stats.wielding_mental)
					break;
				if (powers->powers[actionbar_power].requires_offense_weapon && !stats.wielding_offense)
					break;
				if (powers->powers[actionbar_power].requires_los && !map->collider.line_of_sight(stats.pos.x, stats.pos.y, target.x, target.y))
					break;
				if (powers->powers[actionbar_power].requires_empty_target && !map->collider.is_empty(target.x, target.y))
					break;
												
				current_power = actionbar_power;
				act_target.x = target.x;
				act_target.y = target.y;
			
				// is this a power that requires changing direction?
				if (powers->powers[current_power].face) {
					stats.direction = face(target.x, target.y);
				}
			
				// handle melee powers
				if (powers->powers[current_power].new_state == POWSTATE_SWING) {
					stats.cur_state = AVATAR_MELEE;
					break;
				}
				// handle ranged powers
				if (powers->powers[current_power].new_state == POWSTATE_SHOOT) {
					stats.cur_state = AVATAR_SHOOT;
					break;
				}
				// handle ment powers
				if (powers->powers[current_power].new_state == POWSTATE_CAST) {
					stats.cur_state = AVATAR_CAST;
					break;
				}
				if (powers->powers[current_power].new_state == POWSTATE_BLOCK) {
					stats.cur_state = AVATAR_BLOCK;
					stats.blocking = true;
					break;
				}
			}
			
			break;
			
		case AVATAR_RUN:

			setAnimation("run");
		
			stepfx = rand() % 4;
			
			if (activeAnimation->getCurFrame() == 1 || activeAnimation->getCurFrame() == activeAnimation->getMaxFrame()/2) {
				Mix_PlayChannel(-1, sound_steps[stepfx], 0);
			}

			// allowed to move or use powers?
			if (MOUSE_MOVE) {
				allowed_to_use_power = !(restrictPowerUse && !inp->lock[MAIN1]);
			}
			else {
				allowed_to_use_power = true;
			}
			
			// handle direction changes
			set_direction();
			
			// handle transition to STANCE
			if (!pressing_move()) {
				stats.cur_state = AVATAR_STANCE;
				break;
			} 
			else if (!move()) { // collide with wall
				stats.cur_state = AVATAR_STANCE;
				break;
			}
						
			// handle power usage
			if (allowed_to_use_power && actionbar_power != -1 && stats.cooldown_ticks == 0) {

				target = screen_to_map(inp->mouse.x,  inp->mouse.y + powers->powers[actionbar_power].aim_assist, stats.pos.x, stats.pos.y);
			
				// check requirements
				if (powers->powers[actionbar_power].requires_mp > stats.mp)
					break;
				if (powers->powers[actionbar_power].requires_physical_weapon && !stats.wielding_physical)
					break;
				if (powers->powers[actionbar_power].requires_mental_weapon && !stats.wielding_mental)
					break;
				if (powers->powers[actionbar_power].requires_offense_weapon && !stats.wielding_offense)
					break;
				if (powers->powers[actionbar_power].requires_los && !map->collider.line_of_sight(stats.pos.x, stats.pos.y, target.x, target.y))
					break;
				if (powers->powers[actionbar_power].requires_empty_target && !map->collider.is_empty(target.x, target.y))
					break;
			
				current_power = actionbar_power;
				act_target.x = target.x;
				act_target.y = target.y;
			
				// is this a power that requires changing direction?
				if (powers->powers[current_power].face) {
					stats.direction = face(target.x, target.y);
				}
			
				// handle melee powers
				if (powers->powers[current_power].new_state == POWSTATE_SWING) {
					stats.cur_state = AVATAR_MELEE;
					break;
				}
				// handle ranged powers
				if (powers->powers[current_power].new_state == POWSTATE_SHOOT) {
					stats.cur_state = AVATAR_SHOOT;
					break;
				}
				// handle ment powers
				if (powers->powers[current_power].new_state == POWSTATE_CAST) {
					stats.cur_state = AVATAR_CAST;
					break;
				}
				if (powers->powers[current_power].new_state == POWSTATE_BLOCK) {
					stats.cur_state = AVATAR_BLOCK;
					stats.blocking = true;
					break;
				}				
			}
							
			break;
			
		case AVATAR_MELEE:

			setAnimation("melee");

			if (activeAnimation->getCurFrame() == 1) {
				Mix_PlayChannel(-1, sound_melee, 0);
			}
			
			// do power
			if (activeAnimation->getCurFrame()  == activeAnimation->getMaxFrame()/2) {
				powers->activate(current_power, &stats, act_target);
			}
			
			if (activeAnimation->getTimesPlayed() >= 1) {
				stats.cur_state = AVATAR_STANCE;
				if (stats.haste_duration == 0) stats.cooldown_ticks += stats.cooldown;
			}
			break;

		case AVATAR_CAST:

			setAnimation("ment");

			// do power
			if (activeAnimation->getCurFrame() == activeAnimation->getMaxFrame()/2) {
				powers->activate(current_power, &stats, act_target);
			}

			if (activeAnimation->getTimesPlayed() >= 1) {
				stats.cur_state = AVATAR_STANCE;
				if (stats.haste_duration == 0) stats.cooldown_ticks += stats.cooldown;
			}
			break;

			
		case AVATAR_SHOOT:
		
			setAnimation("ranged");

			// do power
			if (activeAnimation->getCurFrame() == activeAnimation->getMaxFrame()/2) {
				powers->activate(current_power, &stats, act_target);
			}

			if (activeAnimation->getTimesPlayed() >= 1) {
				stats.cur_state = AVATAR_STANCE;
				if (stats.haste_duration == 0) stats.cooldown_ticks += stats.cooldown;
			}
			break;

		case AVATAR_BLOCK:
		
			setAnimation("block");

			if (powers->powers[actionbar_power].new_state != POWSTATE_BLOCK) {
				stats.cur_state = AVATAR_STANCE;
				stats.blocking = false;
			}
			break;
			
		case AVATAR_HIT:

			setAnimation("hit");
						 
			if (activeAnimation->getTimesPlayed() >= 1) {
				stats.cur_state = AVATAR_STANCE;
			}
			
			break;
			
		case AVATAR_DEAD:

			setAnimation("die");
				
			if (activeAnimation->getCurFrame() == 1 && activeAnimation->getTimesPlayed() < 1) {
				Mix_PlayChannel(-1, sound_die, 0);
				log_msg = "You are defeated.  You lose half your gold.  Press Enter to continue.";
			}

			if (activeAnimation->getTimesPlayed() >= 1) {
				stats.corpse = true;
			}
			
			// allow respawn with Accept
			if (inp->pressing[ACCEPT]) {
				stats.hp = stats.maxhp;
				stats.mp = stats.maxmp;
				stats.alive = true;
				stats.corpse = false;
				stats.cur_state = AVATAR_STANCE;
				
				// remove temporary effects
				stats.clearEffects();
				
				// set teleportation variables.  GameEngine acts on these.
				map->teleportation = true;
				map->teleport_mapname = map->respawn_map;
				map->teleport_destination.x = map->respawn_point.x;
				map->teleport_destination.y = map->respawn_point.y;
			}
			
			break;
		
		default:
			break;
	}
	
	// calc new cam position from player position
	// cam is focused at player position
	map->cam.x = stats.pos.x;
	map->cam.y = stats.pos.y;
	map->hero_tile.x = stats.pos.x / 32;
	map->hero_tile.y = stats.pos.y / 32;
	
	// check for map events
	map->checkEvents(stats.pos);
}
Example #2
0
/**
 * logic()
 * Handle a single frame.  This includes:
 * - move the avatar based on buttons pressed
 * - calculate the next frame of animation
 * - calculate camera position based on avatar position
 *
 * @param action The actionbar power activated and the target.  action.power == 0 means no power.
 * @param restrict_power_use Whether or not to allow power usage on mouse1
 * @param npc True if the player is talking to an NPC. Can limit ability to move/attack in certain conditions
 */
void Avatar::logic(std::vector<ActionData> &action_queue, bool restrict_power_use, bool npc) {

	// hazards are processed after Avatar and Enemy[]
	// so process and clear sound effects from previous frames
	// check sound effects
	if (AUDIO) {
		if (play_sfx_phys)
			snd->play(sound_melee, GLOBAL_VIRTUAL_CHANNEL, stats.pos, false);
		if (play_sfx_ment)
			snd->play(sound_mental, GLOBAL_VIRTUAL_CHANNEL, stats.pos, false);
		if (play_sfx_hit)
			snd->play(sound_hit, GLOBAL_VIRTUAL_CHANNEL, stats.pos, false);
		if (play_sfx_die)
			snd->play(sound_die, GLOBAL_VIRTUAL_CHANNEL, stats.pos, false);
		if (play_sfx_critdie)
			snd->play(sound_die, GLOBAL_VIRTUAL_CHANNEL, stats.pos, false);
		if(play_sfx_block)
			snd->play(sound_block, GLOBAL_VIRTUAL_CHANNEL, stats.pos, false);

		// clear sound flags
		play_sfx_hit = false;
		play_sfx_phys = false;
		play_sfx_ment = false;
		play_sfx_die = false;
		play_sfx_critdie = false;
		play_sfx_block = false;
	}

	// clear current space to allow correct movement
	mapr->collider.unblock(stats.pos.x, stats.pos.y);

	// turn on all passive powers
	if ((stats.hp > 0 || stats.effects.triggered_death) && !respawn && !transform_triggered) powers->activatePassives(&stats);
	if (transform_triggered) transform_triggered = false;

	// handle when the player stops blocking
	if (stats.effects.triggered_block && !stats.blocking) {
		stats.cur_state = AVATAR_STANCE;
		stats.effects.triggered_block = false;
		stats.effects.clearTriggerEffects(TRIGGER_BLOCK);
		stats.refresh_stats = true;
	}

	stats.logic();

	bool allowed_to_move;
	bool allowed_to_use_power = true;

#ifdef __ANDROID__
	const bool click_to_respawn = true;
#else
	const bool click_to_respawn = false;
#endif

	// check for revive
	if (stats.hp <= 0 && stats.effects.revive) {
		stats.hp = stats.get(STAT_HP_MAX);
		stats.alive = true;
		stats.corpse = false;
		stats.cur_state = AVATAR_STANCE;
	}

	// check level up
	if (stats.level < static_cast<int>(stats.xp_table.size()) && stats.xp >= stats.xp_table[stats.level]) {
		stats.level_up = true;
		stats.level++;
		std::stringstream ss;
		ss << msg->get("Congratulations, you have reached level %d!", stats.level);
		if (stats.level < stats.max_spendable_stat_points) {
			ss << " " << msg->get("You may increase one attribute through the Character Menu.");
			newLevelNotification = true;
		}
		log_msg = ss.str();
		stats.recalc();
		snd->play(sound_levelup);

		// if the player managed to level up while dead (e.g. via a bleeding creature), restore to life
		if (stats.cur_state == AVATAR_DEAD) {
			stats.cur_state = AVATAR_STANCE;
		}
	}

	// check for bleeding to death
	if (stats.hp == 0 && !(stats.cur_state == AVATAR_DEAD)) {
		stats.effects.triggered_death = true;
		stats.cur_state = AVATAR_DEAD;
	}

	// assist mouse movement
	if (!inpt->pressing[MAIN1]) {
		drag_walking = false;
	}

	// block some interactions when attacking
	if (!inpt->pressing[MAIN1] && !inpt->pressing[MAIN2]) {
		stats.attacking = false;
	}
	else if((inpt->pressing[MAIN1] && !inpt->lock[MAIN1]) || (inpt->pressing[MAIN2] && !inpt->lock[MAIN2])) {
		stats.attacking = true;
	}

	// handle animation
	if (!stats.effects.stun) {
		activeAnimation->advanceFrame();
		for (unsigned i=0; i < anims.size(); i++) {
			if (anims[i] != NULL)
				anims[i]->advanceFrame();
		}
	}

	if (target_anim && target_anim->getTimesPlayed() >= 1) {
		target_visible = false;
		target_anim->reset();
	}

	if (target_anim && target_visible)
		target_anim->advanceFrame();

	// change the cursor if we're attacking
	if (action_queue.empty()) {
		lock_cursor = false;
	}
	else if (lock_cursor) {
		curs->setCursor(CURSOR_ATTACK);
	}

	// save a valid tile position in the event that we untransform on an invalid tile
	if (stats.transformed && mapr->collider.is_valid_position(stats.pos.x,stats.pos.y,MOVEMENT_NORMAL, true)) {
		transform_pos = stats.pos;
		transform_map = mapr->getFilename();
	}

	switch(stats.cur_state) {
		case AVATAR_STANCE:

			setAnimation("stance");

			// allowed to move or use powers?
			if (MOUSE_MOVE) {
				allowed_to_move = restrict_power_use && (!inpt->lock[MAIN1] || drag_walking) && !lockAttack && !npc;
				allowed_to_use_power = !allowed_to_move;
			}
			else {
				allowed_to_move = true;
				allowed_to_use_power = true;
			}

			// handle transitions to RUN
			if (allowed_to_move)
				set_direction();

			if (pressing_move() && allowed_to_move) {
				if (MOUSE_MOVE && inpt->pressing[MAIN1]) {
					inpt->lock[MAIN1] = true;
					drag_walking = true;
				}

				if (move()) { // no collision
					stats.cur_state = AVATAR_RUN;
				}
			}

			if (MOUSE_MOVE && !inpt->pressing[MAIN1]) {
				inpt->lock[MAIN1] = false;
				lockAttack = false;
			}

			break;

		case AVATAR_RUN:

			setAnimation("run");

			if (!sound_steps.empty()) {
				int stepfx = rand() % static_cast<int>(sound_steps.size());

				if (activeAnimation->isFirstFrame() || activeAnimation->isActiveFrame())
					snd->play(sound_steps[stepfx]);
			}

			// allowed to move or use powers?
			if (MOUSE_MOVE) {
				allowed_to_use_power = !(restrict_power_use && !inpt->lock[MAIN1]);
			}
			else {
				allowed_to_use_power = true;
			}

			// handle direction changes
			set_direction();

			// handle transition to STANCE
			if (!pressing_move()) {
				stats.cur_state = AVATAR_STANCE;
				break;
			}
			else if (!move()) { // collide with wall
				stats.cur_state = AVATAR_STANCE;
				break;
			}

			if (activeAnimation->getName() != "run")
				stats.cur_state = AVATAR_STANCE;

			break;

		case AVATAR_ATTACK:

			setAnimation(attack_anim);

			if (MOUSE_MOVE) lockAttack = true;

			if (activeAnimation->isFirstFrame() && attack_anim == "swing")
				snd->play(sound_melee);

			if (activeAnimation->isFirstFrame() && attack_anim == "cast")
				snd->play(sound_mental);

			// do power
			if (activeAnimation->isActiveFrame()) {
				// some powers check if the caster is blocking a tile
				// so we block the player tile prematurely here
				mapr->collider.block(stats.pos.x, stats.pos.y, false);

				powers->activate(current_power, &stats, act_target);
				hero_cooldown[current_power] = powers->getPower(current_power).cooldown;
			}

			if (activeAnimation->getTimesPlayed() >= 1 || activeAnimation->getName() != attack_anim) {
				stats.cur_state = AVATAR_STANCE;
				stats.cooldown_ticks += stats.cooldown;
			}

			break;

		case AVATAR_BLOCK:

			setAnimation("block");

			stats.blocking = false;

			break;

		case AVATAR_HIT:

			setAnimation("hit");

			if (activeAnimation->isFirstFrame()) {
				stats.effects.triggered_hit = true;
			}

			if (activeAnimation->getTimesPlayed() >= 1 || activeAnimation->getName() != "hit") {
				stats.cur_state = AVATAR_STANCE;
			}

			break;

		case AVATAR_DEAD:
			allowed_to_use_power = false;

			if (stats.effects.triggered_death) break;

			if (stats.transformed) {
				stats.transform_duration = 0;
				untransform();
			}

			setAnimation("die");

			if (!stats.corpse && activeAnimation->isFirstFrame() && activeAnimation->getTimesPlayed() < 1) {
				stats.effects.clearEffects();

				// raise the death penalty flag.  Another module will read this and reset.
				stats.death_penalty = true;

				// close menus in GameStatePlay
				close_menus = true;

				snd->play(sound_die);

				if (stats.permadeath) {
					log_msg = msg->get("You are defeated. Game over! Press Enter to exit to Title.");
				}
				else {
					log_msg = msg->get("You are defeated. Press Enter to continue.");
				}

				// if the player is attacking, we need to block further input
				if (inpt->pressing[MAIN1])
					inpt->lock[MAIN1] = true;
			}

			if (activeAnimation->getTimesPlayed() >= 1 || activeAnimation->getName() != "die") {
				stats.corpse = true;
			}

			// allow respawn with Accept if not permadeath
			if (inpt->pressing[ACCEPT] || (click_to_respawn && inpt->pressing[MAIN1] && !inpt->lock[MAIN1])) {
				if (inpt->pressing[ACCEPT]) inpt->lock[ACCEPT] = true;
				if (click_to_respawn && inpt->pressing[MAIN1]) inpt->lock[MAIN1] = true;
				mapr->teleportation = true;
				mapr->teleport_mapname = mapr->respawn_map;
				if (stats.permadeath) {
					// set these positions so it doesn't flash before jumping to Title
					mapr->teleport_destination.x = stats.pos.x;
					mapr->teleport_destination.y = stats.pos.y;
				}
				else {
					respawn = true;

					// set teleportation variables.  GameEngine acts on these.
					mapr->teleport_destination.x = mapr->respawn_point.x;
					mapr->teleport_destination.y = mapr->respawn_point.y;
				}
			}

			break;

		default:
			break;
	}

	// handle power usage
	if (allowed_to_use_power)
		handlePower(action_queue);

	// calc new cam position from player position
	// cam is focused at player position
	mapr->cam.x = stats.pos.x;
	mapr->cam.y = stats.pos.y;

	// check for map events
	mapr->checkEvents(stats.pos);

	// decrement all cooldowns
	for (unsigned i = 0; i < hero_cooldown.size(); i++) {
		hero_cooldown[i]--;
		if (hero_cooldown[i] < 0) hero_cooldown[i] = 0;
	}

	// make the current square solid
	mapr->collider.block(stats.pos.x, stats.pos.y, false);
}