bool leap_actor::call( monster &z ) const { if( !z.can_act() ) { return false; } std::vector<tripoint> options; tripoint target = z.move_target(); float best_float = trig_dist( z.pos(), target ); if( best_float < min_consider_range || best_float > max_consider_range ) { return false; } // We wanted the float for range check // int here will make the jumps more random int best = ( int )best_float; if( !allow_no_target && z.attack_target() == nullptr ) { return false; } for( const tripoint &dest : g->m.points_in_radius( z.pos(), max_range ) ) { if( dest == z.pos() ) { continue; } if( !z.sees( dest ) ) { continue; } if( !g->is_empty( dest ) ) { continue; } int cur_dist = rl_dist( target, dest ); if( cur_dist > best ) { continue; } if( trig_dist( z.pos(), dest ) < min_range ) { continue; } bool blocked_path = false; // check if monster has a clear path to the proposed point std::vector<tripoint> line = g->m.find_clear_path( z.pos(), dest ); for( auto &i : line ) { if( g->m.impassable( i ) ) { blocked_path = true; break; } } if( blocked_path ) { continue; } if( cur_dist < best ) { // Better than any earlier one options.clear(); } options.push_back( dest ); best = cur_dist; } if( options.empty() ) { return false; // Nowhere to leap! } z.moves -= move_cost; const tripoint chosen = random_entry( options ); bool seen = g->u.sees( z ); // We can see them jump... z.setpos( chosen ); seen |= g->u.sees( z ); // ... or we can see them land if( seen ) { add_msg( _( "The %s leaps!" ), z.name().c_str() ); } return true; }
bool bite_actor::call( monster &z ) const { if( !z.can_act() ) { return false; } Creature *target = z.attack_target(); if( target == nullptr || !is_adjacent( z, *target ) ) { return false; } z.mod_moves( -move_cost ); add_msg( m_debug, "%s attempting to bite %s", z.name().c_str(), target->disp_name().c_str() ); int hitspread = target->deal_melee_attack( &z, z.hit_roll() ); if( hitspread < 0 ) { auto msg_type = target == &g->u ? m_warning : m_info; sfx::play_variant_sound( "mon_bite", "bite_miss", sfx::get_heard_volume( z.pos() ), sfx::get_heard_angle( z.pos() ) ); target->add_msg_player_or_npc( msg_type, _( "The %s lunges at you, but you dodge!" ), _( "The %s lunges at <npcname>, but they dodge!" ), z.name().c_str() ); return true; } damage_instance damage = damage_max_instance; dealt_damage_instance dealt_damage; body_part hit; double multiplier = rng_float( min_mul, max_mul ); damage.mult_damage( multiplier ); target->deal_melee_hit( &z, hitspread, false, damage, dealt_damage ); hit = dealt_damage.bp_hit; int damage_total = dealt_damage.total_damage(); add_msg( m_debug, "%s's bite did %d damage", z.name().c_str(), damage_total ); if( damage_total > 0 ) { auto msg_type = target == &g->u ? m_bad : m_info; //~ 1$s is monster name, 2$s bodypart in accusative if( target->is_player() ) { sfx::play_variant_sound( "mon_bite", "bite_hit", sfx::get_heard_volume( z.pos() ), sfx::get_heard_angle( z.pos() ) ); sfx::do_player_death_hurt( *dynamic_cast<player *>( target ), 0 ); } target->add_msg_player_or_npc( msg_type, _( "The %1$s bites your %2$s!" ), _( "The %1$s bites <npcname>'s %2$s!" ), z.name().c_str(), body_part_name_accusative( hit ).c_str() ); if( one_in( no_infection_chance - damage_total ) ) { if( target->has_effect( effect_bite, hit ) ) { target->add_effect( effect_bite, 400, hit, true ); } else if( target->has_effect( effect_infected, hit ) ) { target->add_effect( effect_infected, 250, hit, true ); } else { target->add_effect( effect_bite, 1, hit, true ); } } } else { sfx::play_variant_sound( "mon_bite", "bite_miss", sfx::get_heard_volume( z.pos() ), sfx::get_heard_angle( z.pos() ) ); target->add_msg_player_or_npc( _( "The %1$s bites your %2$s, but fails to penetrate armor!" ), _( "The %1$s bites <npcname>'s %2$s, but fails to penetrate armor!" ), z.name().c_str(), body_part_name_accusative( hit ).c_str() ); } return true; }