Example #1
0
void mdeath::worm( monster &z )
{
    if( g->u.sees( z ) ) {
        if( z.type->dies.size() == 1 ) {
            add_msg( m_good, _( "The %s splits in two!" ), z.name() );
        } else {
            add_msg( m_warning, _( "Two worms crawl out of the %s's corpse." ), z.name() );
        }
    }

    std::vector <tripoint> wormspots;
    for( auto &&wormp : g->m.points_in_radius( z.pos(), 1 ) ) { // *NOPAD*
        if( g->m.has_flag( "DIGGABLE", wormp ) && g->is_empty( wormp ) ) {
            wormspots.push_back( wormp );
        }
    }
    int worms = 0;
    while( worms < 2 && !wormspots.empty() ) {
        const tripoint target = random_entry_removed( wormspots );
        if( !g->critter_at( target ) ) {
            g->summon_mon( mon_halfworm, target );
            worms++;
        }
    }
}
Example #2
0
void mdeath::blobsplit( monster &z )
{
    int speed = z.get_speed() - rng( 30, 50 );
    g->m.spawn_item( z.pos(), "slime_scrap", 1, 0, calendar::turn );
    if( z.get_speed() <= 0 ) {
        if( g->u.sees( z ) ) {
            // TODO: Add vermin-tagged tiny versions of the splattered blob  :)
            add_msg( m_good, _( "The %s splatters apart." ), z.name() );
        }
        return;
    }
    if( g->u.sees( z ) ) {
        if( z.type->dies.size() == 1 ) {
            add_msg( m_good, _( "The %s splits in two!" ), z.name() );
        } else {
            add_msg( m_bad, _( "Two small blobs slither out of the corpse." ) );
        }
    }
    std::vector <tripoint> valid;

    for( auto &&dest : g->m.points_in_radius( z.pos(), 1 ) ) { // *NOPAD*
        if( g->is_empty( dest ) && z.can_move_to( dest ) ) {
            valid.push_back( dest );
        }
    }

    for( int s = 0; s < 2 && !valid.empty(); s++ ) {
        const tripoint target = random_entry_removed( valid );
        if( monster *const blob = g->summon_mon( speed < 50 ? mon_blob_small : mon_blob, target ) ) {
            blob->make_ally( z );
            blob->set_speed_base( speed );
            blob->set_hp( speed );
        }
    }
}
Example #3
0
void mdeath::acid( monster &z )
{
    if( g->u.sees( z ) ) {
        if( z.type->dies.size() ==
            1 ) { //If this death function is the only function. The corpse gets dissolved.
            add_msg( m_mixed, _( "The %s's body dissolves into acid." ), z.name() );
        } else {
            add_msg( m_warning, _( "The %s's body leaks acid." ), z.name() );
        }
    }
    g->m.add_field( z.pos(), fd_acid, 3 );
}
Example #4
0
bool melee_actor::call( monster &z ) const
{
    Creature *target = find_target( z );
    if( target == nullptr ) {
        return false;
    }

    z.mod_moves( -move_cost );

    add_msg( m_debug, "%s attempting to melee_attack %s", z.name().c_str(),
             target->disp_name().c_str() );

    const int acc = accuracy >= 0 ? accuracy : z.type->melee_skill;
    int hitspread = target->deal_melee_attack( &z, dice( acc, 10 ) );

    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, miss_msg_u.c_str(), miss_msg_npc.c_str(),
                                       z.name().c_str() );
        return true;
    }

    damage_instance damage = damage_max_instance;

    double multiplier = rng_float( min_mul, max_mul );
    damage.mult_damage( multiplier );

    body_part bp_hit = body_parts.empty() ?
                       target->select_body_part( &z, hitspread ) :
                       *body_parts.pick();

    target->on_hit( &z, bp_hit );
    dealt_damage_instance dealt_damage = target->deal_damage( &z, bp_hit, damage );
    dealt_damage.bp_hit = bp_hit;

    int damage_total = dealt_damage.total_damage();
    add_msg( m_debug, "%s's melee_attack did %d damage", z.name().c_str(), damage_total );
    if( damage_total > 0 ) {
        on_damage( z, *target, dealt_damage );
    } 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( no_dmg_msg_u.c_str(), no_dmg_msg_npc.c_str(), z.name().c_str(),
                                       body_part_name_accusative( bp_hit ).c_str() );
    }

    return true;
}
bool gun_actor::call( monster &z ) const
{
    Creature *target;
    if( z.friendly != 0 ) {
        // Attacking monsters, not the player!
        int boo_hoo;
        target = z.auto_find_hostile_target( range, boo_hoo );
        if( target == nullptr ) {
            // Couldn't find any targets!
            if( boo_hoo > 0 && g->u.sees( z ) ) {
                // because that stupid oaf was in the way!
                add_msg( m_warning, ngettext( "Pointed in your direction, the %s emits an IFF warning beep.",
                                              "Pointed in your direction, the %s emits %d annoyed sounding beeps.",
                                              boo_hoo ),
                         z.name().c_str(), boo_hoo );
            }
            return false;
        }

        shoot( z, *target );
        return true;
    }

    // Not friendly; hence, firing at the player too
    target = z.attack_target();
    if( target == nullptr || rl_dist( z.pos(), target->pos() ) > range ||
        !z.sees( *target ) ) {
        return false;
    }

    shoot( z, *target );
    return true;
}
Example #6
0
void mdeath::boomer_glow( monster &z )
{
    std::string explode = string_format( _( "a %s explode!" ), z.name() );
    sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "small" );

    for( auto &&dest : g->m.points_in_radius( z.pos(), 1 ) ) { // *NOPAD*
        g->m.bash( dest, 10 );
        if( monster *const z = g->critter_at<monster>( dest ) ) {
            z->stumble();
            z->moves -= 250;
        }
        if( Creature *const critter = g->critter_at( dest ) ) {
            critter->add_env_effect( effect_boomered, bp_eyes, 5, 25_turns );
            for( int i = 0; i < rng( 2, 4 ); i++ ) {
                body_part bp = random_body_part();
                critter->add_env_effect( effect_glowing, bp, 4, 4_minutes );
                if( critter != nullptr && critter->has_effect( effect_glowing ) ) {
                    break;
                }
            }
        }
    }

    g->m.propagate_field( z.pos(), fd_bile, 30, 2 );
}
Example #7
0
void mdefense::zapback( monster &m, Creature *const source,
                        dealt_projectile_attack const *const proj )
{
    // Not a melee attack, attacker lucked out or out of range
    if( source == nullptr || proj != nullptr ||
        rng( 0, 100 ) > m.def_chance || rl_dist( m.pos(), source->pos() ) > 1 ) {
        return;
    }

    if( source->is_elec_immune() ) {
        return;
    }

    // Players/NPCs can avoid the shock by using non-conductive weapons
    player const *const foe = dynamic_cast<player *>( source );
    if( foe != nullptr && !foe->weapon.conductive() && !foe->unarmed_attack() ) {
        return;
    }

    damage_instance const shock {
        DT_ELECTRIC, static_cast<float>( rng( 1, 5 ) )
    };
    source->deal_damage( &m, bp_arm_l, shock );
    source->deal_damage( &m, bp_arm_r, shock );

    if( g->u.sees( source->pos() ) ) {
        auto const msg_type = ( source == &g->u ) ? m_bad : m_info;
        add_msg( msg_type, _( "Striking the %1$s shocks %2$s!" ),
                 m.name().c_str(), source->disp_name().c_str() );
    }
    source->check_dead_state();
}
Example #8
0
void mdeath::darkman( monster &z )
{
    g->u.remove_effect( effect_darkness );
    if( g->u.sees( z ) ) {
        add_msg( m_good, _( "The %s melts away." ), z.name() );
    }
}
Example #9
0
void mdeath::broken_ammo( monster &z )
{
    if( g->u.sees( z.pos() ) ) {
        //~ %s is the possessive form of the monster's name
        add_msg( m_info, _( "The %s's interior compartment sizzles with destructive energy." ),
                 z.name() );
    }
    mdeath::broken( z );
}
Example #10
0
void mdeath::conflagration( monster &z )
{
    for( const auto &dest : g->m.points_in_radius( z.pos(), 1 ) ) {
        g->m.propagate_field( dest, fd_fire, 18, 3 );
    }
    const std::string explode = string_format( _( "a %s explode!" ), z.name() );
    sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "small" );

}
Example #11
0
void mdeath::fireball( monster &z )
{
    if( one_in( 10 ) ) {
        g->m.propagate_field( z.pos(), fd_fire, 15, 3 );
        std::string explode = string_format( _( "an explosion of tank of the %s's flamethrower!" ),
                                             z.name() );
        sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "default" );
        add_msg( m_good, _( "I love the smell of burning zed in the morning." ) );
    } else {
        normal( z );
    }
}
void Creature_tracker::remove( const monster &critter )
{
    const auto iter = std::find_if( monsters_list.begin(), monsters_list.end(),
    [&]( const std::shared_ptr<monster> &ptr ) {
        return ptr.get() == &critter;
    } );
    if( iter == monsters_list.end() ) {
        debugmsg( "Tried to remove invalid monster %s", critter.name() );
        return;
    }

    remove_from_location_map( critter );
    monsters_list.erase( iter );
}
Example #13
0
void mdeath::broken( monster &z ) {
    // Bail out if flagged (simulates eyebot flying away)
    if( z.no_corpse_quiet ) {
        return;
    }
    std::string item_id = z.type->id.str();
    if (item_id.compare(0, 4, "mon_") == 0) {
        item_id.erase(0, 4);
    }
    // make "broken_manhack", or "broken_eyebot", ...
    item_id.insert(0, "broken_");
    g->m.spawn_item( z.pos(), item_id, 1, 0, calendar::turn );
    if( g->u.sees( z.pos() ) ) {
        add_msg( m_good, _( "The %s collapses!" ), z.name().c_str() );
    }
}
Example #14
0
void mdeath::boomer( monster &z )
{
    std::string explode = string_format( _( "a %s explode!" ), z.name() );
    sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "small" );
    for( auto &&dest : g->m.points_in_radius( z.pos(), 1 ) ) { // *NOPAD*
        g->m.bash( dest, 10 );
        if( monster *const z = g->critter_at<monster>( dest ) ) {
            z->stumble();
            z->moves -= 250;
        }
    }

    if( rl_dist( z.pos(), g->u.pos() ) == 1 ) {
        g->u.add_env_effect( effect_boomered, bp_eyes, 2, 24_turns );
    }

    g->m.propagate_field( z.pos(), fd_bile, 15, 1 );
}
Example #15
0
void melee_actor::on_damage( monster &z, Creature &target, dealt_damage_instance &dealt ) const
{
    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 ), false );
    }
    auto msg_type = target.attitude_to( g->u ) == Creature::A_FRIENDLY ? m_bad : m_neutral;
    const body_part bp = dealt.bp_hit;
    target.add_msg_player_or_npc( msg_type, hit_dmg_u, hit_dmg_npc, z.name(),
                                  body_part_name_accusative( bp ) );

    for( const auto &eff : effects ) {
        if( x_in_y( eff.chance, 100 ) ) {
            const body_part affected_bp = eff.affect_hit_bp ? bp : eff.bp;
            target.add_effect( eff.id, time_duration::from_turns( eff.duration ), affected_bp, eff.permanent );
        }
    }
}
Example #16
0
bool gun_actor::call( monster &z ) const
{
    Creature *target;

    if( z.friendly ) {
        int max_range = 0;
        for( const auto &e : ranges ) {
            max_range = std::max( std::max( max_range, e.first.first ), e.first.second );
        }

        int hostiles; // hostiles which cannot be engaged without risking friendly fire
        target = z.auto_find_hostile_target( max_range, hostiles );
        if( !target ) {
            if( hostiles > 0 && g->u.sees( z ) ) {
                add_msg( m_warning, ngettext( "Pointed in your direction, the %s emits an IFF warning beep.",
                                              "Pointed in your direction, the %s emits %d annoyed sounding beeps.",
                                              hostiles ),
                         z.name(), hostiles );
            }
            return false;
        }

    } else {
        target = z.attack_target();
        if( !target || !z.sees( *target ) ) {
            return false;
        }
    }

    int dist = rl_dist( z.pos(), target->pos() );
    for( const auto &e : ranges ) {
        if( dist >= e.first.first && dist <= e.first.second ) {
            shoot( z, *target, e.second );
            return true;
        }
    }
    return false;
}
void mdefense::zapback( monster &m, Creature *const source,
                        dealt_projectile_attack const * )
{
    if( source == nullptr ) {
        return;
    }
    player const *const foe = dynamic_cast<player *>( source );

    // Players/NPCs can avoid the shock by using non-conductive weapons
    if( foe != nullptr && foe->is_armed() && !foe->weapon.conductive() ) {
        return;
    }

    // Ranged weapons get no zapback, unless they have an active MELEE mode.
    if( foe != nullptr && foe->weapon.is_gun() && !foe->weapon.gun_current_mode().melee() ) {
        return;
    }

    if( source->is_elec_immune() ) {
        return;
    }

    if( g->u.sees( source->pos() ) ) {
        auto const msg_type = ( source == &g->u ) ? m_bad : m_info;
        add_msg( msg_type, _( "Striking the %1$s shocks %2$s!" ),
                 m.name().c_str(), source->disp_name().c_str() );
    }

    damage_instance const shock {
        DT_ELECTRIC, static_cast<float>( rng( 1, 5 ) )
    };
    source->deal_damage( &m, bp_arm_l, shock );
    source->deal_damage( &m, bp_arm_r, shock );

    source->check_dead_state();
}
Example #18
0
void mdeath::normal( monster &z )
{
    if( z.no_corpse_quiet ) {
        return;
    }

    if( z.type->in_species( ZOMBIE ) ) {
        sfx::play_variant_sound( "mon_death", "zombie_death", sfx::get_heard_volume( z.pos() ) );
    }

    if( g->u.sees( z ) ) {
        //Currently it is possible to get multiple messages that a monster died.
        add_msg( m_good, _( "The %s dies!" ), z.name() );
    }

    const int max_hp = std::max( z.get_hp_max(), 1 );
    const float overflow_damage = std::max( -z.get_hp(), 0 );
    const float corpse_damage = 2.5 * overflow_damage / max_hp;
    const bool pulverized = corpse_damage > 5 && overflow_damage > z.get_hp_max();

    z.bleed(); // leave some blood if we have to

    if( !pulverized ) {
        make_mon_corpse( z, static_cast<int>( std::floor( corpse_damage * itype::damage_scale ) ) );
    }
    // if mdeath::splatter was set along normal makes sure it is not called twice
    bool splatt = false;
    for( const auto &deathfunction : z.type->dies ) {
        if( deathfunction == mdeath::splatter ) {
            splatt = true;
        }
    }
    if( !splatt ) {
        splatter( z );
    }
}
Example #19
0
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;
}
Example #20
0
void mdeath::guilt( monster &z )
{
    const int MAX_GUILT_DISTANCE = 5;
    int kill_count = g->kill_count( z.type->id );
    int maxKills = 100; // this is when the player stop caring altogether.

    // different message as we kill more of the same monster
    std::string msg = _( "You feel guilty for killing %s." ); // default guilt message
    game_message_type msgtype = m_bad; // default guilt message type
    std::map<int, std::string> guilt_tresholds;
    guilt_tresholds[75] = _( "You feel ashamed for killing %s." );
    guilt_tresholds[50] = _( "You regret killing %s." );
    guilt_tresholds[25] = _( "You feel remorse for killing %s." );

    if( g->u.has_trait( trait_PSYCHOPATH ) || g->u.has_trait( trait_PRED3 ) ||
        g->u.has_trait( trait_PRED4 ) ) {
        return;
    }
    if( rl_dist( z.pos(), g->u.pos() ) > MAX_GUILT_DISTANCE ) {
        // Too far away, we can deal with it.
        return;
    }
    if( z.get_hp() >= 0 ) {
        // We probably didn't kill it
        return;
    }
    if( kill_count >= maxKills ) {
        // player no longer cares
        if( kill_count == maxKills ) {
            //~ Message after killing a lot of monsters which would normally affect the morale negatively. %s is the monster name, it will be pluralized with a number of 100.
            add_msg( m_good, _( "After killing so many bloody %s you no longer care "
                                "about their deaths anymore." ), z.name( maxKills ) );
        }
        return;
    } else if( ( g->u.has_trait( trait_PRED1 ) ) || ( g->u.has_trait( trait_PRED2 ) ) ) {
        msg = ( _( "Culling the weak is distasteful, but necessary." ) );
        msgtype = m_neutral;
    } else {
        msgtype = m_bad;
        for( auto &guilt_treshold : guilt_tresholds ) {
            if( kill_count >= guilt_treshold.first ) {
                msg = guilt_treshold.second;
                break;
            }
        }
    }

    add_msg( msgtype, msg, z.name() );

    int moraleMalus = -50 * ( 1.0 - ( static_cast<float>( kill_count ) / maxKills ) );
    int maxMalus = -250 * ( 1.0 - ( static_cast<float>( kill_count ) / maxKills ) );
    time_duration duration = 30_minutes * ( 1.0 - ( static_cast<float>( kill_count ) / maxKills ) );
    time_duration decayDelay = 3_minutes * ( 1.0 - ( static_cast<float>( kill_count ) / maxKills ) );
    if( z.type->in_species( ZOMBIE ) ) {
        moraleMalus /= 10;
        if( g->u.has_trait( trait_PACIFIST ) ) {
            moraleMalus *= 5;
        } else if( g->u.has_trait( trait_PRED1 ) ) {
            moraleMalus /= 4;
        } else if( g->u.has_trait( trait_PRED2 ) ) {
            moraleMalus /= 5;
        }
    }
    g->u.add_morale( MORALE_KILLED_MONSTER, moraleMalus, maxMalus, duration, decayDelay );

}
Example #21
0
void gun_actor::shoot( monster &z, Creature &target ) const
{
    // Make sure our ammo isn't weird.
    if( z.ammo[ammo_type] > max_ammo ) {
        debugmsg( "Generated too much ammo (%d) of type %s for %s in gun_actor::shoot",
                  z.ammo[ammo_type], ammo_type.c_str(), z.name().c_str() );
        z.ammo[ammo_type] = max_ammo;
    }

    const bool require_targeting = ( require_targeting_player && target.is_player() ) ||
                                   ( require_targeting_npc && target.is_npc() ) ||
                                   ( require_targeting_monster && target.is_monster() );
    const bool not_targeted = require_targeting && !z.has_effect( effect_targeted );
    const bool not_laser_locked = require_targeting && laser_lock &&
                                  !target.has_effect( effect_was_laserlocked );

    if( not_targeted || not_laser_locked ) {
        if( !targeting_sound.empty() ) {
            sounds::sound( z.pos(), targeting_volume, _( targeting_sound.c_str() ) );
        }
        if( not_targeted ) {
            z.add_effect( effect_targeted, targeting_timeout );
        }
        if( not_laser_locked ) {
            target.add_effect( effect_laserlocked, 5 );
            target.add_effect( effect_was_laserlocked, 5 );
            target.add_msg_if_player( m_warning,
                                      _( "You're not sure why you've got a laser dot on you..." ) );
        }

        z.moves -= targeting_cost;
        return;
    }

    // It takes a while
    z.moves -= move_cost;

    if( z.ammo[ammo_type] <= 0 && !no_ammo_sound.empty() ) {
        sounds::sound( z.pos(), 10, _( no_ammo_sound.c_str() ) );
        return;
    }

    if( g->u.sees( z ) ) {
        add_msg( m_warning, _( description.c_str() ) );
    }

    npc tmp;
    tmp.name = _( "The " ) + z.name();
    tmp.set_fake( true );
    tmp.recoil = 0;
    tmp.driving_recoil = 0;
    tmp.setpos( z.pos() );
    tmp.str_max = fake_str;
    tmp.dex_max = fake_dex;
    tmp.int_max = fake_int;
    tmp.per_max = fake_per;
    tmp.str_cur = fake_str;
    tmp.dex_cur = fake_dex;
    tmp.int_cur = fake_int;
    tmp.per_cur = fake_per;
    tmp.weapon = item( gun_type, 0 );
    tmp.weapon.set_curammo( ammo_type );
    tmp.weapon.charges = z.ammo[ammo_type];
    if( z.friendly != 0 ) {
        tmp.attitude = NPCATT_DEFEND;
    } else {
        tmp.attitude = NPCATT_KILL;
    }

    for( const auto &pr : fake_skills ) {
        tmp.skillLevel( pr.first ).level( pr.second );
    }

    const auto distance = rl_dist( z.pos(), target.pos() );
    int burst_size = std::min( burst_limit, tmp.weapon.burst_size() );
    if( distance > range_no_burst || burst_size < 1 ) {
        burst_size = 1;
    }

    tmp.fire_gun( target.pos(), burst_size );
    z.ammo[ammo_type] = tmp.weapon.charges;
    if( require_targeting ) {
        z.add_effect( effect_targeted, targeting_timeout_extend );
    }

    if( laser_lock ) {
        // To prevent spamming laser locks when the player can tank that stuff somehow
        target.add_effect( effect_was_laserlocked, 5 );
    }
}
Example #22
0
void shoot_monster(game *g, player &p, monster &mon, int &dam, double goodhit, item* weapon)
{
 // Gunmods don't have a type, so use the player weapon type.
 it_gun* firing = dynamic_cast<it_gun*>(p.weapon.type);
 std::string message;
 bool u_see_mon = g->u_see(&(mon));
 if (mon.has_flag(MF_HARDTOSHOOT) && !one_in(4) &&
     weapon->curammo->phase != LIQUID &&
     weapon->curammo->accuracy >= 4) { // Buckshot hits anyway
  if (u_see_mon)
   g->add_msg("The shot passes through the %s without hitting.",
           mon.name().c_str());
  goodhit = 1;
 } else { // Not HARDTOSHOOT
// Armor blocks BEFORE any critical effects.
  int zarm = mon.armor_cut();
  zarm -= weapon->curammo->pierce;
  if (weapon->curammo->phase == LIQUID)
   zarm = 0;
  else if (weapon->curammo->accuracy < 4) // Shot doesn't penetrate armor well
   zarm *= rng(2, 4);
  if (zarm > 0)
   dam -= zarm;
  if (dam <= 0) {
   if (u_see_mon)
    g->add_msg("The shot reflects off the %s!",
            mon.name_with_armor().c_str());
   dam = 0;
   goodhit = 1;
  }
  if (goodhit < .1 && !mon.has_flag(MF_NOHEAD)) {
   message = "Headshot!";
   dam = rng(5 * dam, 8 * dam);
   p.practice(g->turn, firing->skill_used, 5);
  } else if (goodhit < .2) {
   message = "Critical!";
   dam = rng(dam * 2, dam * 3);
   p.practice(g->turn, firing->skill_used, 2);
  } else if (goodhit < .4) {
   dam = rng(int(dam * .9), int(dam * 1.5));
   p.practice(g->turn, firing->skill_used, rng(0, 2));
  } else if (goodhit <= .7) {
   message = "Grazing hit.";
   dam = rng(0, dam);
  } else
   dam = 0;
// Find the zombie at (x, y) and hurt them, MAYBE kill them!
  if (dam > 0) {
   mon.moves -= dam * 5;
   if (&p == &(g->u) && u_see_mon)
    g->add_msg("%s You hit the %s for %d damage.", message.c_str(), mon.name().c_str(), dam);
   else if (u_see_mon)
    g->add_msg("%s %s shoots the %s.", message.c_str(), p.name.c_str(), mon.name().c_str());

   bool bMonDead = mon.hurt(dam);
   hit_animation(mon.posx - g->u.posx + VIEWX - g->u.view_offset_x,
                 mon.posy - g->u.posy + VIEWY - g->u.view_offset_y,
                 red_background(mon.type->color), (bMonDead) ? '%' : mon.symbol());

   if (bMonDead)
    g->kill_mon(g->mon_at(mon.posx, mon.posy), (&p == &(g->u)));
   else if (weapon->curammo->ammo_effects != 0)
    g->hit_monster_with_flags(mon, weapon->curammo->ammo_effects);

   dam = 0;
  }
 }
}
Example #23
0
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;
}
Example #24
0
void mdeath::disintegrate( monster &z )
{
    if( g->u.sees( z ) ) {
        add_msg( m_good, _( "The %s disintegrates!" ), z.name() );
    }
}
Example #25
0
void mdeath::normal( monster &z )
{
    if( z.no_corpse_quiet ) {
        return;
    }

    if( z.type->in_species( ZOMBIE ) ) {
        sfx::play_variant_sound( "mon_death", "zombie_death", sfx::get_heard_volume( z.pos() ) );
    }

    if( g->u.sees( z ) ) {
        //Currently it is possible to get multiple messages that a monster died.
        add_msg( m_good, _( "The %s dies!" ), z.name().c_str() );
    }

    const int max_hp = std::max( z.get_hp_max(), 1 );
    const float overflow_damage = std::max( -z.get_hp(), 0 );
    const float corpse_damage = 2.5 * overflow_damage / max_hp;
    const bool pulverized = corpse_damage > 5 && overflow_damage > z.get_hp_max();

    z.bleed(); // leave some blood if we have to

    if( !pulverized ) {
        make_mon_corpse( z, int( std::floor( corpse_damage ) ) );
    }

    // Limit chunking to flesh, veggy and insect creatures until other kinds are supported.
    const std::vector<material_id> gib_mats = {{
        material_id( "flesh" ), material_id( "hflesh" ),
        material_id( "veggy" ), material_id( "iflesh" ),
        material_id( "bone" )
    }};
    const bool gibbable = !z.type->has_flag( MF_NOGIB ) &&
        std::any_of( gib_mats.begin(), gib_mats.end(), [&z]( const material_id &gm ) {
            return z.made_of( gm );
        } );

    const field_id type_blood = z.bloodType();
    const field_id type_gib = z.gibType();

    if( gibbable ) {
        const auto area = g->m.points_in_radius( z.pos(), 1 );
        int number_of_gibs = std::min( std::floor( corpse_damage ) - 1, 1 + max_hp / 5.0f );

        if( pulverized && z.type->size >= MS_MEDIUM ) {
            number_of_gibs += rng( 1, 6 );
            sfx::play_variant_sound( "mon_death", "zombie_gibbed", sfx::get_heard_volume( z.pos() ) );
        }

        for( int i = 0; i < number_of_gibs; ++i ) {
            g->m.add_splatter( type_gib, random_entry( area ), rng( 1, i + 1 ) );
            g->m.add_splatter( type_blood, random_entry( area ) );
        }
    }

    const int num_chunks = z.type->get_meat_chunks_count();

    if( pulverized && gibbable ) {
        const itype_id meat = z.type->get_meat_itype();
        const item chunk( meat );
        for( int i = 0; i < num_chunks; i++ ) {
            tripoint tarp( z.pos() + point( rng( -3, 3 ), rng( -3, 3 ) ) );
            const auto traj = line_to( z.pos(), tarp );

            for( size_t j = 0; j < traj.size(); j++ ) {
                tarp = traj[j];
                if( one_in( 2 ) && type_blood != fd_null ) {
                    g->m.add_splatter( type_blood, tarp );
                } else {
                    g->m.add_splatter( type_gib, tarp, rng( 1, j + 1 ) );
                }
                if( g->m.impassable( tarp ) ) {
                    g->m.bash( tarp, 3 );
                    if( g->m.impassable( tarp ) ) {
                        // Target is obstacle, not destroyed by bashing,
                        // stop trajectory in front of it, if this is the first
                        // point (e.g. wall adjacent to monster) , make it invalid.
                        if( j > 0 ) {
                            tarp = traj[j - 1];
                        } else {
                            tarp = tripoint_min;
                        }
                        break;
                    }
                }
            }

            if( tarp != tripoint_min ) {
                g->m.add_item_or_charges( tarp, chunk );
            }
        }
    }
}
Example #26
0
void mdeath::melt( monster &z )
{
    if( g->u.sees( z ) ) {
        add_msg( m_good, _( "The %s melts away." ), z.name() );
    }
}
Example #27
0
void mdeath::disappear( monster &z )
{
    if( g->u.sees( z ) ) {
        add_msg( m_good, _( "The %s disappears." ), z.name() );
    }
}
Example #28
0
void mdeath::detonate( monster &z )
{
    weighted_int_list<std::string> amm_list;
    for( const auto &amm : z.ammo ) {
        amm_list.add( amm.first, amm.second );
    }

    std::vector<std::string> pre_dets;
    for( int i = 0; i < 3; i++ ) {
        if( amm_list.get_weight() <= 0 ) {
            break;
        }
        // Grab one item
        std::string tmp = *amm_list.pick();
        // and reduce its weight by 1
        amm_list.add_or_replace( tmp, amm_list.get_specific_weight( tmp ) - 1 );
        // and stash it for use
        pre_dets.push_back( tmp );
    }

    // Update any hardcoded explosion equivalencies
    std::vector<std::pair<std::string, long>> dets;
    for( const std::string &bomb_id : pre_dets ) {
        if( bomb_id == "bot_grenade_hack" ) {
            dets.push_back( std::make_pair( "grenade_act", 5 ) );
        } else if( bomb_id == "bot_flashbang_hack" ) {
            dets.push_back( std::make_pair( "flashbang_act", 5 ) );
        } else if( bomb_id == "bot_gasbomb_hack" ) {
            dets.push_back( std::make_pair( "gasbomb_act", 20 ) );
        } else if( bomb_id == "bot_c4_hack" ) {
            dets.push_back( std::make_pair( "c4armed", 10 ) );
        } else if( bomb_id == "bot_mininuke_hack" ) {
            dets.push_back( std::make_pair( "mininuke_act", 20 ) );
        } else {
            // Get the transformation item
            const iuse_transform *actor = dynamic_cast<const iuse_transform *>(
                                              item::find_type( bomb_id )->get_use( "transform" )->get_actor_ptr() );
            if( actor == nullptr ) {
                // Invalid bomb item, move to the next ammo item
                add_msg( m_debug, "Invalid bomb type in detonate mondeath for %s.", z.name() );
                continue;
            }
            dets.emplace_back( actor->target, actor->ammo_qty );
        }
    }

    if( g->u.sees( z ) ) {
        if( dets.empty() ) {
            //~ %s is the possessive form of the monster's name
            add_msg( m_info,
                     _( "The %s's hands fly to its pockets, but there's nothing left in them." ),
                     z.name() );
        } else {
            //~ %s is the possessive form of the monster's name
            add_msg( m_bad, _( "The %s's hands fly to its remaining pockets, opening them!" ),
                     z.name() );
        }
    }
    // HACK, used to stop them from having ammo on respawn
    z.add_effect( effect_no_ammo, 1_turns, num_bp, true );

    // First die normally
    mdeath::normal( z );
    // Then detonate our suicide bombs
    for( const auto &bombs : dets ) {
        item bomb_item( bombs.first, 0 );
        bomb_item.charges = bombs.second;
        bomb_item.active = true;
        g->m.add_item_or_charges( z.pos(), bomb_item );
    }
}
Example #29
0
void mdeath::gameover( monster &z )
{
    add_msg( m_bad, _( "The %s was destroyed!  GAME OVER!" ), z.name() );
    g->u.hp_cur[hp_torso] = 0;
}
Example #30
0
void mdeath::smokeburst( monster &z )
{
    std::string explode = string_format( _( "a %s explode!" ), z.name() );
    sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "small" );
    g->m.emit_field( z.pos(), emit_id( "emit_smoke_blast" ) );
}