string do_attack(monster_t& attacker, monster_t& defender)
	{
	int dmg = calc_damage(attacker, defender);
				
	int def_hp = attacker.max_hp * attacker.count;
	def_hp -= dmg;
	int killed = ceil(dmg / (float) defender.max_hp);
	defender.count = ceil(def_hp / (float) defender.max_hp);
	
	
	std::stringstream strm;
	strm << game_t::_get_monster_name(attacker.type) << "s do " << dmg << ".  " 
			<< killed << " " << game_t::_get_monster_name(defender.type) << "s perish.";
	return strm.str();
	}
void curr_attack() {
	// see if nv status allow for attack
	switch (CURR_POKEMON->nv.nvstatus) {
		case NON_S:
		case BRN_S:
		case PSN_S:
		case TXC_S:
			break;
		
		case FRZ_S:
			if (roll(.2)) {
				sprintf(msg, "%s thawed out!", CURR_PNAME); send();
				CURR_POKEMON->nv.nvstatus = NON_S;
				break;
			} else {
				sprintf(msg, "%s is frozen solid!", CURR_PNAME); send();
				return;
			}
		case PAR_S:
			if (roll(.25)) {
				sprintf(msg, "%s is fully paralyzed!", CURR_PNAME); send();
				return;
			} else {
				break;
			}
		case SLP_S:
			if (CURR_POKEMON->nv.nv_arg > 0) {
				sprintf(msg, "%s is fast asleep!", CURR_PNAME); send();
				CURR_POKEMON->nv.nv_arg--;
				return;
			} else {
				sprintf(msg, "%s woke up!", CURR_PNAME); send();
				CURR_POKEMON->nv.nvstatus = NON_S;
				break;
			}
		case FNT_S: // should never happen
		default:
			return;
	}

	// see if v status allows for attack
	if (CURR_POKEMON->v.is_flinch) {
		sprintf(msg, "%s flinched!", CURR_PNAME); send();
		CURR_POKEMON->v.is_flinch = false;
		return;
	}
	if (CURR_POKEMON->v.is_recharge) {
		sprintf(msg, "%s is recharging!", CURR_PNAME); send();
		CURR_POKEMON->v.is_recharge = false;
		return;
	}
	if (CURR_POKEMON->v.is_confuse) {
		sprintf(msg, "%s is confused!", CURR_PNAME); send();
		if (roll(.25)) { // this is not quite how it works in Pokemon
			sprintf(msg, "%s snapped out of confusion!", CURR_PNAME); send();
			CURR_POKEMON->v.is_confuse = false;
		} else {
			if (roll(.5)) {
				sprintf(msg, "%s hurt itself in confusion!", CURR_PNAME); send();
				int damage = calc_damage(CURR_POKEMON, CURR_POKEMON, 40, PHYSICAL_MT);
				// printf("[%s applied %i damage to himself]", CURR_PNAME, damage);
				apply_damage(CURR_POKEMON, damage);
				return;
			}
		}
	}

	move_s *move = curr_move();
	sprintf(msg, "%s used %s!", CURR_PNAME, move->name); send();

	// check if unique
	if (move->unique) {
		switch (move->unique) {
			default: // no unique moves at the moment
				return;
		}
	}

	// check accuracy
	if (is_aggressive(move)) {
		if (!roll(move->accuracy * calc_accuracy(CURR_POKEMON, OTHR_POKEMON))) { // swift support?
			sprintf(msg, "It missed!"); send();
			return;
		}

		if (move->movetype != STATUS_MT) {
			int damage = calc_damage(CURR_POKEMON, OTHR_POKEMON, move->damage, move->movetype);

			double stab_bonus = (has_type(CURR_POKEMON, move->type) ? 1.5 : 1); // STAB bonus
			double effective_bonus = calc_effective(move->type, OTHR_POKEMON); // type bonus

			double crit_chance = (move->effect == HIGH_CRIT_E2 ? .125 : .0625);
			double crit_bonus = (roll(crit_chance) ? 1.5 : 1.0); // this is not quite how it works in Pokemon

			double rndm = (rand() % 16 + 85) / 100.0;

			// printf("%i, %lf, %lf, %lf, %lf", damage, stab_bonus, effective_bonus, crit_bonus, rndm);
			int total_damage = (int)(damage * stab_bonus * effective_bonus * crit_bonus * rndm);

			// if burnt, physical damage output is halfed
			if (CURR_POKEMON->nv.nvstatus == BRN_S && move->movetype == PHYSICAL_MT) {
				total_damage /= 2;
			}

			if (effective_bonus > 1) {
				sprintf(msg, "It's super effective!"); send();
			} else if (effective_bonus == 0) {
				sprintf(msg, "It has no effect!"); send();
			} else if (effective_bonus < 1) {
				sprintf(msg, "It's not very effective!"); send();
			}

			if (crit_bonus > 1) {
				sprintf(msg, "Critical hit!"); send();
			}

			// printf("[%s applies %i damage to %s]", CURR_PNAME, total_damage, OTHR_PNAME);
			apply_damage(OTHR_POKEMON, total_damage);
		} else {
			if (calc_effective(move->type, OTHR_POKEMON) == 0) { // thunder wave can't work on ground
				sprintf(msg, "It has no effect!"); send();
				return;
			}
		}
	}

	int calculation; // can be used for some of the switch cases

	// check secondary effect
	if (move->effect != NON_E2) {
		if (roll(move->chance)) {
			switch (move->effect) {
				case APPLY_BRN_E2:
					apply_nvstatus(OTHR_POKEMON, BRN_S);
					break;
				case APPLY_FRZ_E2:
					apply_nvstatus(OTHR_POKEMON, FRZ_S);
					break;
				case APPLY_PAR_E2:
					apply_nvstatus(OTHR_POKEMON, PAR_S);
					break;
				case APPLY_PSN_E2:
					apply_nvstatus(OTHR_POKEMON, PSN_S);
					break;
				case APPLY_TXC_E2:
					apply_nvstatus(OTHR_POKEMON, TXC_S);
					break;
				case APPLY_SLP_E2:
					apply_nvstatus(OTHR_POKEMON, SLP_S);
					break;
				case APPLY_FLINCH_E2:
					OTHR_POKEMON->v.is_flinch = true;
					break;
				case APPLY_CONFUSE_E2:
					sprintf(msg, "%s became confused!", OTHR_PNAME); send();
					OTHR_POKEMON->v.is_confuse = true;
					break;
				case HIGH_CRIT_E2:
					break; // this doesn't happen here
				case SELF_KILL_E2:
					apply_nvstatus(CURR_POKEMON, FNT_S);
					break;
				case SELF_ATTACK_E2:
					apply_attackstage(CURR_POKEMON, move->m_arg);
					break;
				case SELF_DEFENSE_E2:
					apply_defensestage(CURR_POKEMON, move->m_arg);
					break;
				case SELF_SATTACK_E2:
					apply_sattackstage(CURR_POKEMON, move->m_arg);
					break;
				case SELF_SDEFENSE_E2:
					apply_sdefensestage(CURR_POKEMON, move->m_arg);
					break;
				case SELF_SPEED_E2:
					apply_speedstage(CURR_POKEMON, move->m_arg);
					break;
				case SELF_ACCURACY_E2:
					apply_accuracystage(CURR_POKEMON, move->m_arg);
					break;
				case SELF_EVASION_E2:
					apply_evasionstage(CURR_POKEMON, move->m_arg);
					break;
				case OTHR_ATTACK_E2:
					apply_attackstage(OTHR_POKEMON, move->m_arg);
					break;
				case OTHR_DEFENSE_E2:
					apply_defensestage(OTHR_POKEMON, move->m_arg);
					break;
				case OTHR_SATTACK_E2:
					apply_sattackstage(OTHR_POKEMON, move->m_arg);
					break;
				case OTHR_SDEFENSE_E2:
					apply_sdefensestage(OTHR_POKEMON, move->m_arg);
					break;
				case OTHR_SPEED_E2:
					apply_speedstage(OTHR_POKEMON, move->m_arg);
					break;
				case OTHR_ACCURACY_E2:
					apply_accuracystage(OTHR_POKEMON, move->m_arg);
					break;
				case OTHR_EVASION_E2:
					apply_evasionstage(OTHR_POKEMON, move->m_arg);
					break;
				case SELF_ATTACK_SATTACK_E2:
					apply_attackstage(CURR_POKEMON, move->m_arg);
					apply_sattackstage(CURR_POKEMON, move->m_arg);
					break;
				case HAZE_E2:
					reset_stages(CURR_POKEMON);
					reset_stages(OTHR_POKEMON);
					break;
				case RECOIL_E2:
					calculation = battle.last_dmg / move->m_arg; // pos is recoil, neg is heal
					if (calculation > 0) {
						sprintf(msg, "%s was hit with recoil!", CURR_PNAME); send();
						// printf("[%s took %i damage in recoil]", CURR_PNAME, calculation);
					} else if (calculation < 0) {
						sprintf(msg, "%s regained health!", CURR_PNAME); send();
						// printf("[%s restored %i damage]", CURR_PNAME, calculation);
					}
					apply_damage(CURR_POKEMON, calculation);
					break; // this doesn't happen here
				case RECHARGE_E2:
					CURR_POKEMON->v.is_recharge = true;
					break;
				case NON_E2: // shouldn't happen
					break;
			}
		}
	}
}