void mdefense::acidsplash( monster &m, Creature *const source, dealt_projectile_attack const *const proj ) { // Would be useful to have the attack data here, for cutting vs. bashing etc. if( proj != nullptr && proj->dealt_dam.total_damage() <= 0 ) { // Projectile didn't penetrate the target, no acid will splash out of it. return; } if( proj != nullptr && !one_in( 3 ) ) { return; //Less likely for a projectile to deliver enough force } size_t num_drops = rng( 4, 6 ); player const *const foe = dynamic_cast<player *>( source ); if( proj == nullptr && foe != nullptr ) { if( foe->weapon.is_melee( DT_CUT ) || foe->weapon.is_melee( DT_STAB ) ) { num_drops += rng( 3, 4 ); } if( foe->unarmed_attack() ) { damage_instance const burn { DT_ACID, static_cast<float>( rng( 1, 5 ) ) }; if( one_in( 2 ) ) { source->deal_damage( &m, bp_hand_l, burn ); } else { source->deal_damage( &m, bp_hand_r, burn ); } source->add_msg_if_player( m_bad, _( "Acid covering %s burns your hand!" ), m.disp_name().c_str() ); } } tripoint initial_target = source == nullptr ? m.pos() : source->pos(); // Don't splatter directly on the `m`, that doesn't work well auto pts = closest_tripoints_first( 1, initial_target ); pts.erase( std::remove( pts.begin(), pts.end(), m.pos() ), pts.end() ); projectile prj; prj.speed = 10; prj.range = 4; prj.proj_effects.insert( "DRAW_AS_LINE" ); prj.proj_effects.insert( "NO_DAMAGE_SCALING" ); prj.impact.add_damage( DT_ACID, rng( 1, 3 ) ); for( size_t i = 0; i < num_drops; i++ ) { const tripoint &target = random_entry( pts ); projectile_attack( prj, m.pos(), target, { 1200 } ); } if( g->u.sees( m.pos() ) ) { add_msg( m_warning, _( "Acid sprays out of %s as it is hit!" ), m.disp_name().c_str() ); } }
bool Creature_tracker::update_pos( const monster &critter, const tripoint &new_pos ) { const auto old_pos = critter.pos(); if( critter.is_dead() ) { // mon_at ignores dead critters anyway, changing their position in the // monsters_by_location map is useless. remove_from_location_map( critter ); return true; } const int critter_id = mon_at( old_pos ); const int new_critter_id = mon_at( new_pos ); if( new_critter_id >= 0 ) { auto &othermon = *monsters_list[new_critter_id]; if( othermon.is_hallucination() ) { othermon.die( nullptr ); } else { debugmsg( "update_zombie_pos: wanted to move %s to %d,%d,%d, but new location already has %s", critter.disp_name().c_str(), new_pos.x, new_pos.y, new_pos.z, othermon.disp_name().c_str() ); return false; } } if( critter_id >= 0 ) { if( &critter == monsters_list[critter_id] ) { monsters_by_location.erase( old_pos ); monsters_by_location[new_pos] = critter_id; return true; } else { const auto &othermon = *monsters_list[critter_id]; debugmsg( "update_zombie_pos: wanted to move %s from old location %d,%d,%d, but it had %s instead", critter.disp_name().c_str(), old_pos.x, old_pos.y, old_pos.z, othermon.disp_name().c_str() ); return false; } } else { // We're changing the x/y/z coordinates of a zombie that hasn't been added // to the game yet. add_zombie() will update monsters_by_location for us. debugmsg( "update_zombie_pos: no %s at %d,%d,%d (moving to %d,%d,%d)", critter.disp_name().c_str(), old_pos.x, old_pos.y, old_pos.z, new_pos.x, new_pos.y, new_pos.z ); // Rebuild cache in case the monster actually IS in the game, just bugged rebuild_cache(); return false; } return false; }
bool Creature_tracker::update_pos( const monster &critter, const tripoint &new_pos ) { if( critter.is_dead() ) { // find ignores dead critters anyway, changing their position in the // monsters_by_location map is useless. remove_from_location_map( critter ); return true; } if( const std::shared_ptr<monster> new_critter_ptr = find( new_pos ) ) { auto &othermon = *new_critter_ptr; if( othermon.is_hallucination() ) { othermon.die( nullptr ); } else { debugmsg( "update_zombie_pos: wanted to move %s to %d,%d,%d, but new location already has %s", critter.disp_name(), new_pos.x, new_pos.y, new_pos.z, othermon.disp_name() ); return false; } } 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() ) { monsters_by_location.erase( critter.pos() ); monsters_by_location[new_pos] = *iter; return true; } else { const tripoint &old_pos = critter.pos(); // We're changing the x/y/z coordinates of a zombie that hasn't been added // to the game yet. add_zombie() will update monsters_by_location for us. debugmsg( "update_zombie_pos: no %s at %d,%d,%d (moving to %d,%d,%d)", critter.disp_name(), old_pos.x, old_pos.y, old_pos.z, new_pos.x, new_pos.y, new_pos.z ); // Rebuild cache in case the monster actually IS in the game, just bugged rebuild_cache(); return false; } }
void mdeath::jabberwock( monster &z ) { player *ch = dynamic_cast<player*>( z.get_killer() ); bool vorpal = ch && ch->is_player() && rl_dist( z.pos(), ch->pos() ) <= 1 && ch->weapon.has_flag( "DIAMOND" ) && ch->weapon.volume() > units::from_milliliter( 750 ); if( vorpal && !ch->weapon.has_technique( matec_id( "VORPAL" ) ) ) { if( ch->sees( z ) ) { //~ %s is the possessive form of the monster's name ch->add_msg_if_player( m_info, _( "As the flames in %s eyes die out, your weapon seems to shine slightly brighter." ), z.disp_name( true ).c_str() ); } ch->weapon.add_technique( matec_id( "VORPAL" ) ); } mdeath::normal(z); }