// MATERIALS-TODO: use fire resistance void game::hit_monster_with_flags(monster &z, const std::set<std::string> &effects) { if (effects.count("FLAME")) { if (z.made_of("veggy") || z.made_of("cotton") || z.made_of("wool") || z.made_of("paper") || z.made_of("wood")) z.add_effect(ME_ONFIRE, rng(8, 20)); else if (z.made_of("flesh")) z.add_effect(ME_ONFIRE, rng(5, 10)); } else if (effects.count("INCENDIARY")) { if (z.made_of("veggy") || z.made_of("cotton") || z.made_of("wool") || z.made_of("paper") || z.made_of("wood")) z.add_effect(ME_ONFIRE, rng(2, 6)); else if (z.made_of("flesh") && one_in(4)) z.add_effect(ME_ONFIRE, rng(1, 4)); } else if (effects.count("IGNITE")) { if (z.made_of("veggy") || z.made_of("cotton") || z.made_of("wool") || z.made_of("paper") || z.made_of("wood")) z.add_effect(ME_ONFIRE, rng(6, 6)); else if (z.made_of("flesh")) z.add_effect(ME_ONFIRE, rng(10, 10)); } if (effects.count("BOUNCE")) { z.add_effect(ME_BOUNCED, 1); } int stun_strength = 0; if (effects.count("BEANBAG")) { stun_strength = 4; } if (effects.count("LARGE_BEANBAG")) { stun_strength = 16; } if( stun_strength > 0 ) { switch( z.type->size ) { case MS_TINY: stun_strength *= 4; break; case MS_SMALL: stun_strength *= 2; break; case MS_MEDIUM: default: break; case MS_LARGE: stun_strength /= 2; break; case MS_HUGE: stun_strength /= 4; break; } z.add_effect( ME_STUNNED, rng(stun_strength / 2, stun_strength) ); } }
void stash_on_pet( const std::list<item> &items, monster &pet ) { units::volume remaining_volume = pet.inv.empty() ? 0_ml : pet.inv.front().get_storage(); units::mass remaining_weight = pet.weight_capacity(); for( const auto &it : pet.inv ) { remaining_volume -= it.volume(); remaining_weight -= it.weight(); } for( auto &it : items ) { pet.add_effect( effect_controlled, 5_turns ); if( it.volume() > remaining_volume ) { add_msg( m_bad, _( "%1$s did not fit and fell to the %2$s." ), it.display_name(), g->m.name( pet.pos() ) ); g->m.add_item_or_charges( pet.pos(), it ); } else if( it.weight() > remaining_weight ) { add_msg( m_bad, _( "%1$s is too heavy and fell to the %2$s." ), it.display_name(), g->m.name( pet.pos() ) ); g->m.add_item_or_charges( pet.pos(), it ); } else { pet.add_item( it ); remaining_volume -= it.volume(); remaining_weight -= it.weight(); } } }
void bite_actor::on_damage( monster &z, Creature &target, dealt_damage_instance &dealt ) const { melee_actor::on_damage( z, target, dealt ); if( target.has_effect( effect_grabbed ) && one_in( no_infection_chance - dealt.total_damage() ) ) { const body_part hit = dealt.bp_hit; if( target.has_effect( effect_bite, hit ) ) { target.add_effect( effect_bite, 40_minutes, hit, true ); } else if( target.has_effect( effect_infected, hit ) ) { target.add_effect( effect_infected, 25_minutes, hit, true ); } else { target.add_effect( effect_bite, 1_turns, hit, true ); } } if( target.has_trait( trait_id( "TOXICFLESH" ) ) ) { z.add_effect( effect_poison, 5_minutes ); z.add_effect( effect_badpoison, 5_minutes ); } }
void game::hit_monster_with_flags(monster &z, unsigned int effects) { if (effects & mfb(AMMO_FLAME)) { if (z.made_of(VEGGY) || z.made_of(COTTON) || z.made_of(WOOL) || z.made_of(PAPER) || z.made_of(WOOD)) z.add_effect(ME_ONFIRE, rng(8, 20)); else if (z.made_of(FLESH)) z.add_effect(ME_ONFIRE, rng(5, 10)); } else if (effects & mfb(AMMO_INCENDIARY)) { if (z.made_of(VEGGY) || z.made_of(COTTON) || z.made_of(WOOL) || z.made_of(PAPER) || z.made_of(WOOD)) z.add_effect(ME_ONFIRE, rng(2, 6)); else if (z.made_of(FLESH) && one_in(4)) z.add_effect(ME_ONFIRE, rng(1, 4)); } }
// MATERIALS-TODO: use fire resistance void game::hit_monster_with_flags(monster &z, unsigned int effects) { if (effects & mfb(AMMO_FLAME)) { if (z.made_of("veggy") || z.made_of("cotton") || z.made_of("wool") || z.made_of("paper") || z.made_of("wood")) z.add_effect(ME_ONFIRE, rng(8, 20)); else if (z.made_of("flesh")) z.add_effect(ME_ONFIRE, rng(5, 10)); } else if (effects & mfb(AMMO_INCENDIARY)) { if (z.made_of("veggy") || z.made_of("cotton") || z.made_of("wool") || z.made_of("paper") || z.made_of("wood")) z.add_effect(ME_ONFIRE, rng(2, 6)); else if (z.made_of("flesh") && one_in(4)) z.add_effect(ME_ONFIRE, rng(1, 4)); } else if (effects & mfb(AMMO_IGNITE)) { if (z.made_of("veggy") || z.made_of("cotton") || z.made_of("wool") || z.made_of("paper") || z.made_of("wood")) z.add_effect(ME_ONFIRE, rng(6, 6)); else if (z.made_of("flesh")) z.add_effect(ME_ONFIRE, rng(10, 10)); } }
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 ); } }
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 ); } }
void gun_actor::shoot( monster &z, Creature &target, const gun_mode_id &mode ) const { if( require_sunlight && !g->is_in_sunlight( z.pos() ) ) { if( one_in( 3 ) && g->u.sees( z ) ) { add_msg( _( failure_msg ), z.name() ); } return; } 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_volume > 0 && !targeting_sound.empty() ) { sounds::sound( z.pos(), targeting_volume, sounds::sound_t::alarm, _( targeting_sound ) ); } if( not_targeted ) { z.add_effect( effect_targeted, time_duration::from_turns( targeting_timeout ) ); } if( not_laser_locked ) { target.add_effect( effect_laserlocked, 5_turns ); target.add_effect( effect_was_laserlocked, 5_turns ); 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; } z.moves -= move_cost; item gun( gun_type ); gun.gun_set_mode( mode ); itype_id ammo = ( ammo_type != "null" ) ? ammo_type : gun.ammo_default(); if( ammo != "null" ) { gun.ammo_set( ammo, z.ammo[ ammo ] ); } if( !gun.ammo_sufficient() ) { if( !no_ammo_sound.empty() ) { sounds::sound( z.pos(), 10, sounds::sound_t::combat, _( no_ammo_sound ) ); } return; } standard_npc tmp( _( "The " ) + z.name(), {}, 8, fake_str, fake_dex, fake_int, fake_per ); tmp.set_fake( true ); tmp.setpos( z.pos() ); tmp.set_attitude( z.friendly ? NPCATT_FOLLOW : NPCATT_KILL ); tmp.recoil = 0; // no need to aim for( const auto &pr : fake_skills ) { tmp.set_skill_level( pr.first, pr.second ); } tmp.weapon = gun; tmp.i_add( item( "UPS_off", calendar::turn, 1000 ) ); if( g->u.sees( z ) ) { add_msg( m_warning, _( description ), z.name(), tmp.weapon.tname() ); } z.ammo[ammo] -= tmp.fire_gun( target.pos(), gun.gun_current_mode().qty ); if( require_targeting ) { z.add_effect( effect_targeted, time_duration::from_turns( 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_turns ); } }
void shoot_monster(player &p, monster &mon, int &dam, double goodhit, item* weapon, const std::set<std::string> &effects) { // 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)); int adjusted_damage = dam; if (mon.has_flag(MF_HARDTOSHOOT) && !one_in(10 - 10 * (.8 - goodhit)) && // Maxes out at 50% chance with perfect hit weapon->curammo->phase != LIQUID && !effects.count("SHOT") && !effects.count("BOUNCE")) { if (u_see_mon) g->add_msg(_("The shot passes through the %s without hitting."), mon.name().c_str()); } else { // Not HARDTOSHOOT // Bounce applies whether it does damage or not. if (effects.count("BOUNCE")) { mon.add_effect("bounced", 1); } // Armor blocks BEFORE any critical effects. int zarm = mon.get_armor_cut(bp_torso); zarm -= weapon->gun_pierce(); if (weapon->curammo->phase == LIQUID) zarm = 0; else if (effects.count("SHOT")) // Shot doesn't penetrate armor well zarm *= rng(2, 3); if (zarm > 0) adjusted_damage -= zarm; if (adjusted_damage <= 0) { if (u_see_mon) g->add_msg(_("The shot reflects off the %s!"), mon.name_with_armor().c_str()); adjusted_damage = 0; goodhit = 1; } if (goodhit <= .1 && !mon.has_flag(MF_NOHEAD)) { message = _("Headshot!"); adjusted_damage = rng(5 * adjusted_damage, 8 * adjusted_damage); p.practice(g->turn, firing->skill_used, 5); p.lifetime_stats()->headshots++; } else if (goodhit <= .2) { message = _("Critical!"); adjusted_damage = rng(adjusted_damage * 2, adjusted_damage * 3); p.practice(g->turn, firing->skill_used, 3); } else if (goodhit <= .4) { message = _("Good hit!"); adjusted_damage = rng(adjusted_damage , adjusted_damage * 2); p.practice(g->turn, firing->skill_used, 2); } else if (goodhit <= .6) { adjusted_damage = rng(adjusted_damage / 2, adjusted_damage); p.practice(g->turn, firing->skill_used, 1); } else if (goodhit <= .8) { message = _("Grazing hit."); adjusted_damage = rng(0, adjusted_damage); } else { adjusted_damage = 0; } if(item(weapon->curammo, 0).has_flag("NOGIB")) { adjusted_damage = std::min(adjusted_damage, mon.hp+10); } // Find the zombie at (x, y) and hurt them, MAYBE kill them! if (adjusted_damage > 0) { switch (mon.type->size) { case MS_TINY: mon.moves -= rng(0, adjusted_damage * 5); break; case MS_SMALL: mon.moves -= rng(0, adjusted_damage * 3); break; case MS_MEDIUM: mon.moves -= rng(0, adjusted_damage); break; case MS_LARGE: mon.moves -= rng(0, adjusted_damage / 3); break; case MS_HUGE: mon.moves -= rng(0, adjusted_damage / 5); break; } 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(), adjusted_damage); } else if (u_see_mon) { g->add_msg(_("%s %s shoots the %s."), message.c_str(), p.name.c_str(), mon.name().c_str()); } g->hit_monster_with_flags(mon, effects); damage_instance d; d.add_damage(DT_CUT, adjusted_damage, weapon->gun_pierce(), effects.count("SHOT")?rng(2,3):1); // Shot doesn't penetrate armor well mon.deal_damage(&p, bp_torso, -1, d); if( u_see_mon ) { g->draw_hit_mon(mon.posx(), mon.posy(), mon, mon.is_dead_state()); } } } dam = adjusted_damage; }