void unit_attack(display * disp, game_board & board, const map_location& a, const map_location& b, int damage, const attack_type& attack, const attack_type* secondary_attack, int swing,std::string hit_text,int drain_amount,std::string att_text, const std::vector<std::string>* extra_hit_sounds) { if(!disp ||disp->video().update_locked() || disp->video().faked() || (disp->fogged(a) && disp->fogged(b)) || preferences::show_combat() == false) { return; } //const unit_map& units = disp->get_units(); disp->select_hex(map_location::null_location()); // scroll such that there is at least half a hex spacing around fighters disp->scroll_to_tiles(a,b,game_display::ONSCREEN,true,0.5,false); log_scope("unit_attack"); const unit_map::const_iterator att = board.units().find(a); assert(att.valid()); const unit& attacker = *att; const unit_map::iterator def = board.find_unit(b); assert(def.valid()); unit &defender = *def; int def_hitpoints = defender.hitpoints(); att->set_facing(a.get_relative_dir(b)); def->set_facing(b.get_relative_dir(a)); defender.set_facing(b.get_relative_dir(a)); unit_animator animator; unit_ability_list leaders = attacker.get_abilities("leadership"); unit_ability_list helpers = defender.get_abilities("resistance"); std::string text = number_and_text(damage, hit_text); std::string text_2 = number_and_text(abs(drain_amount), att_text); unit_animation::hit_type hit_type; if(damage >= defender.hitpoints()) { hit_type = unit_animation::hit_type::KILL; } else if(damage > 0) { hit_type = unit_animation::hit_type::HIT; }else { hit_type = unit_animation::hit_type::MISS; } animator.add_animation(&attacker, "attack", att->get_location(), def->get_location(), damage, true, text_2, (drain_amount >= 0) ? display::rgb(0, 255, 0) : display::rgb(255, 0, 0), hit_type, &attack, secondary_attack, swing); // note that we take an anim from the real unit, we'll use it later const unit_animation *defender_anim = def->anim_comp().choose_animation(*disp, def->get_location(), "defend", att->get_location(), damage, hit_type, &attack, secondary_attack, swing); animator.add_animation(&defender, defender_anim, def->get_location(), true, text , display::rgb(255, 0, 0)); for (const unit_ability & ability : leaders) { if(ability.second == a) continue; if(ability.second == b) continue; unit_map::const_iterator leader = board.units().find(ability.second); assert(leader.valid()); leader->set_facing(ability.second.get_relative_dir(a)); animator.add_animation(&*leader, "leading", ability.second, att->get_location(), damage, true, "", 0, hit_type, &attack, secondary_attack, swing); } for (const unit_ability & ability : helpers) { if(ability.second == a) continue; if(ability.second == b) continue; unit_map::const_iterator helper = board.units().find(ability.second); assert(helper.valid()); helper->set_facing(ability.second.get_relative_dir(b)); animator.add_animation(&*helper, "resistance", ability.second, def->get_location(), damage, true, "", 0, hit_type, &attack, secondary_attack, swing); } animator.start_animations(); animator.wait_until(0); int damage_left = damage; bool extra_hit_sounds_played = false; while(damage_left > 0 && !animator.would_end()) { if(!extra_hit_sounds_played && extra_hit_sounds != nullptr) { for (std::string hit_sound : *extra_hit_sounds) { sound::play_sound(hit_sound); } extra_hit_sounds_played = true; } int step_left = (animator.get_end_time() - animator.get_animation_time() )/50; if(step_left < 1) step_left = 1; int removed_hp = damage_left/step_left ; if(removed_hp < 1) removed_hp = 1; defender.take_hit(removed_hp); damage_left -= removed_hp; animator.wait_until(animator.get_animation_time_potential() +50); } animator.wait_for_end(); // pass the animation back to the real unit def->anim_comp().start_animation(animator.get_end_time(), defender_anim, true); reset_helpers(&*att, &*def); def->set_hitpoints(def_hitpoints); }
void unit_attack( const map_location& a, const map_location& b, int damage, const attack_type& attack, const attack_type* secondary_attack, int swing,std::string hit_text,bool drain,std::string att_text) { game_display* disp = game_display::get_singleton(); if(!disp ||disp->video().update_locked() || disp->video().faked() || (disp->fogged(a) && disp->fogged(b)) || preferences::show_combat() == false) { return; } unit_map& units = disp->get_units(); disp->select_hex(map_location::null_location); // scroll such that there is at least half a hex spacing around fighters disp->scroll_to_tiles(a,b,game_display::ONSCREEN,true,0.5,false); log_scope("unit_attack"); const unit_map::iterator att = units.find(a); assert(att != units.end()); unit& attacker = *att; const unit_map::iterator def = units.find(b); assert(def != units.end()); unit &defender = *def; int def_hitpoints = defender.hitpoints(); att->set_facing(a.get_relative_dir(b)); def->set_facing(b.get_relative_dir(a)); defender.set_facing(b.get_relative_dir(a)); unit_animator animator; unit_ability_list leaders = attacker.get_abilities("leadership"); unit_ability_list helpers = defender.get_abilities("resistance"); std::string text ; if(damage) text = lexical_cast<std::string>(damage); if(!hit_text.empty()) { text.insert(text.begin(),hit_text.size()/2,' '); text = text + "\n" + hit_text; } std::string text_2 ; if(drain && damage) text_2 = lexical_cast<std::string>(std::min<int>(damage,defender.hitpoints())/2); if(!att_text.empty()) { text_2.insert(text_2.begin(),att_text.size()/2,' '); text_2 = text_2 + "\n" + att_text; } unit_animation::hit_type hit_type; if(damage >= defender.hitpoints()) { hit_type = unit_animation::KILL; } else if(damage > 0) { hit_type = unit_animation::HIT; }else { hit_type = unit_animation::MISS; } animator.add_animation(&attacker, "attack", att->get_location(), def->get_location(), damage, true, text_2, display::rgb(0, 255, 0), hit_type, &attack, secondary_attack, swing); // note that we take an anim from the real unit, we'll use it later const unit_animation *defender_anim = def->choose_animation(*disp, def->get_location(), "defend", att->get_location(), damage, hit_type, &attack, secondary_attack, swing); animator.add_animation(&defender, defender_anim, def->get_location(), true, text , display::rgb(255, 0, 0)); for (std::vector<std::pair<const config *, map_location> >::iterator itor = leaders.cfgs.begin(); itor != leaders.cfgs.end(); ++itor) { if(itor->second == a) continue; if(itor->second == b) continue; unit_map::iterator leader = units.find(itor->second); assert(leader != units.end()); leader->set_facing(itor->second.get_relative_dir(a)); animator.add_animation(&*leader, "leading", itor->second, att->get_location(), damage, true, "", 0, hit_type, &attack, secondary_attack, swing); } for (std::vector<std::pair<const config *, map_location> >::iterator itor = helpers.cfgs.begin(); itor != helpers.cfgs.end(); ++itor) { if(itor->second == a) continue; if(itor->second == b) continue; unit_map::iterator helper = units.find(itor->second); assert(helper != units.end()); helper->set_facing(itor->second.get_relative_dir(b)); animator.add_animation(&*helper, "resistance", itor->second, def->get_location(), damage, true, "", 0, hit_type, &attack, secondary_attack, swing); } animator.start_animations(); animator.wait_until(0); int damage_left = damage; while(damage_left > 0 && !animator.would_end()) { int step_left = (animator.get_end_time() - animator.get_animation_time() )/50; if(step_left < 1) step_left = 1; int removed_hp = damage_left/step_left ; if(removed_hp < 1) removed_hp = 1; defender.take_hit(removed_hp); damage_left -= removed_hp; animator.wait_until(animator.get_animation_time_potential() +50); } animator.wait_for_end(); // pass the animation back to the real unit def->start_animation(animator.get_end_time(), defender_anim, true); reset_helpers(&*att, &*def); def->set_hitpoints(def_hitpoints); }