void Entity::attack(Entity* target) { if (!target) { debugmsg("'%s' attempted attack() on a null target."); return; } Attack att = std_attack(); action_points -= att.speed; bool you_see = GAME.player->can_sense(GAME.map, posx, posy); bool attacker_is_you = is_you(); std::string miss_verb = (attacker_is_you ? "miss" : "misses"); if (hit_roll(att.to_hit) < target->dodge_roll()) { if (you_see) { std::stringstream msg; msg << get_name_to_player() << " " << miss_verb << " " << target->get_name_to_player() << "!"; GAME.add_msg( msg.str().c_str() ); } // TODO: action_point penalty for missing? return; } Body_part bp_hit = (target->is_player() ? random_body_part_to_hit() : BODYPART_NULL); // TODO: Should total_damage be reduced by damage absorbed by armor? Damage_set damage = att.roll_damage(); for (int i = 0; i < DAMAGE_MAX; i++) { int dam = damage.get_damage(i); target->take_damage(Damage_type(i), dam, get_name_to_player(), bp_hit); } if (you_see) { std::stringstream damage_ss; damage_ss << get_name_to_player() << " "; if (attacker_is_you) { damage_ss << att.verb_first; } else { damage_ss << att.verb_third; } damage_ss << " "; if (bp_hit == BODYPART_NULL) { damage_ss << target->get_name_to_player(); } else { damage_ss << target->get_possessive() << " " << body_part_name(bp_hit); } if (target->is_you()) { damage_ss << " for " << damage.total_damage() << " damage"; } damage_ss << "!"; GAME.add_msg( damage_ss.str().c_str() ); } }
void Game::launch_projectile(Item it, Ranged_attack attack, Point origin, Point target) { int range = rl_dist(origin, target); int angle_missed_by = attack.roll_variance(); // Use 1800 since attack.variance is measured in 10ths of a degree double distance_missed_by = range * tan(angle_missed_by * PI / 1800); int tiles_off = int(distance_missed_by); if (tiles_off >= 1) { target.x += rng(0 - tiles_off, tiles_off); target.y += rng(0 - tiles_off, tiles_off); } // fine_distance is used later to see if we hit the target or "barely missed" int fine_distance = 100 * (distance_missed_by - tiles_off); debugmsg("angle %d, missed %f, tiles %d, fine %d", angle_missed_by, distance_missed_by, tiles_off, fine_distance); std::vector<Point> path = map->line_of_sight(origin, target); if (path.empty()) { // Lost line of sight at some point path = line_to(origin, target); } for (int i = 0; i < path.size(); i++) { if (map->move_cost(path[i].x, path[i].y) == 0) { // It's a solid tile, so let's try to smash through it! map->smash(path[i].x, path[i].y, attack.roll_damage(), false); if (map->move_cost(path[i].x, path[i].y) == 0) { return; // We didn't make it! } } else { Entity* entity_hit = entities.entity_at(path[i].x, path[i].y); if (entity_hit) { bool hit; // TODO: Incorporate the size of the monster if (i == path.size() - 1) { hit = rng(0, 100) >= fine_distance; } else { hit = one_in(3); } if (hit) { add_msg("You shoot %s!", entity_hit->get_name_to_player().c_str()); Damage_set dam = attack.roll_damage(); entity_hit->take_damage(DAMAGE_PIERCE, dam.get_damage(DAMAGE_PIERCE), "you"); return; } else if (i == path.size() - 1) { add_msg("You barely miss %s.", entity_hit->get_name_to_player().c_str()); } } } } }