bool repair_part( vehicle &veh, vehicle_part &pt, Character &who_c ) { // @todo: Get rid of this cast after moving relevant functions down to Character player &who = ( player & )who_c; int part_index = veh.index_of_part( &pt ); auto &vp = pt.info(); // @todo: Expose base part damage somewhere, don't recalculate it here const auto reqs = pt.is_broken() ? vp.install_requirements() : vp.repair_requirements() * pt.damage_level( 4 ); inventory map_inv; map_inv.form_from_map( who.pos(), PICKUP_RANGE ); if( !reqs.can_make_with_inventory( who.crafting_inventory() ) ) { who.add_msg_if_player( m_info, _( "You don't meet the requirements to repair the %s." ), pt.name().c_str() ); return false; } // consume items extracting any base item (which we will need if replacing broken part) item base( vp.item ); for( const auto &e : reqs.get_components() ) { for( auto &obj : who.consume_items( who.select_item_component( e, 1, map_inv ), 1 ) ) { if( obj.typeId() == vp.item ) { base = obj; } } } for( const auto &e : reqs.get_tools() ) { who.consume_tools( who.select_tool_component( e, 1, map_inv ), 1 ); } who.invalidate_crafting_inventory(); for( const auto &sk : pt.is_broken() ? vp.install_skills : vp.repair_skills ) { who.practice( sk.first, calc_xp_gain( vp, sk.first ) ); } // If part is broken, it will be destroyed and references invalidated std::string partname = pt.name(); if( pt.is_broken() ) { const int dir = pt.direction; point loc = pt.mount; auto replacement_id = pt.info().get_id(); g->m.spawn_items( who.pos(), pt.pieces_for_broken_part() ); veh.remove_part( part_index ); const int partnum = veh.install_part( loc, replacement_id, std::move( base ) ); veh.parts[partnum].direction = dir; veh.part_removal_cleanup(); } else { veh.set_hp( pt, pt.info().durability ); } // @todo: NPC doing that who.add_msg_if_player( m_good, _( "You repair the %1$s's %2$s." ), veh.name.c_str(), partname.c_str() ); return true; }
bool vehicle::can_enable( const vehicle_part &pt, bool alert ) const { if( std::none_of( parts.begin(), parts.end(), [&pt]( const vehicle_part & e ) { return &e == &pt; } ) || pt.removed ) { debugmsg( "Cannot enable removed or non-existent part" ); } if( pt.is_broken() ) { return false; } if( pt.info().has_flag( "PLANTER" ) && !warm_enough_to_plant() ) { if( alert ) { add_msg( m_bad, _( "It is too cold to plant anything now." ) ); } return false; } // @todo: check fuel for combustion engines if( pt.info().epower < 0 && fuel_left( fuel_type_battery, true ) <= 0 ) { if( alert ) { add_msg( m_bad, _( "Insufficient power to enable %s" ), pt.name() ); } return false; } return true; }
turret_data vehicle::turret_query( vehicle_part &pt ) { if( !pt.is_turret() || pt.removed || pt.is_broken() ) { return turret_data(); } return turret_data( this, &pt ); }
bool vehicle::mod_hp( vehicle_part &pt, int qty, damage_type dt ) { if( pt.info().durability > 0 ) { return pt.base.mod_damage( -( pt.base.max_damage() * qty / pt.info().durability ), dt ); } else { return false; } }
void vehicle::set_hp( vehicle_part &pt, int qty ) { if( qty == pt.info().durability || pt.info().durability <= 0 ) { pt.base.set_damage( 0 ); } else if( qty == 0 ) { pt.base.set_damage( pt.base.max_damage() ); } else { pt.base.set_damage( pt.base.max_damage() - pt.base.max_damage() * qty / pt.info().durability ); } }
int vehicle::turret_fire( vehicle_part &pt ) { int shots = 0; item &gun = pt.base; if( !gun.is_gun() ) { return false; } turret_reload( pt ); switch( turret_query( pt ) ) { case turret_status::no_ammo: add_msg( m_bad, string_format( _( "The %s is out of ammo." ), pt.name().c_str() ).c_str() ); break; case turret_status::no_power: add_msg( m_bad, string_format( _( "The %s is not powered." ), pt.name().c_str() ).c_str() ); break; case turret_status::ready: { // Clone the shooter and place them at turret on roof auto shooter = g->u; shooter.setpos( global_part_pos3( pt ) ); shooter.add_effect( effect_on_roof, 1 ); shooter.recoil = abs( velocity ) / 100 / 4; tripoint pos = shooter.pos(); auto trajectory = g->pl_target_ui( pos, gun.gun_range(), &gun, TARGET_MODE_TURRET_MANUAL ); g->draw_ter(); if( !trajectory.empty() ) { auto mode = gun.gun_current_mode(); shots = shooter.fire_gun( trajectory.back(), mode.qty, *mode ); } break; } default: debugmsg( "unknown turret status" ); break; } turret_unload( pt ); drain( fuel_type_battery, gun.get_gun_ups_drain() * shots ); return shots; }
bool vehicle::assign_seat( vehicle_part &pt, const npc &who ) { if( !pt.is_seat() || !pt.set_crew( who ) ) { return false; } // NPC's can only be assigned to one seat in the vehicle for( auto &e : parts ) { if( &e == &pt ) { continue; // skip this part } if( e.is_seat() ) { const npc *n = e.crew(); if( n && n->getID() == who.getID() ) { e.unset_crew(); } } } return true; }
int vehicle::automatic_fire_turret( vehicle_part &pt ) { auto gun = turret_query( pt ); if( gun.query() != turret_data::status::ready ) { return 0; } tripoint pos = global_part_pos3( pt ); npc tmp; tmp.set_fake( true ); tmp.name = rmp_format( _( "<veh_player>The %s" ), pt.name().c_str() ); tmp.set_skill_level( gun.base()->gun_skill(), 8 ); tmp.set_skill_level( skill_id( "gun" ), 4 ); tmp.recoil = abs( velocity ) / 100 / 4; tmp.setpos( pos ); tmp.str_cur = 16; tmp.dex_cur = 8; tmp.per_cur = 12; // Assume vehicle turrets are friendly to the player. tmp.attitude = NPCATT_FOLLOW; int area = aoe_size( gun.ammo_effects() ); if( area > 0 ) { area += area == 1 ? 1 : 2; // Pad a bit for less friendly fire } tripoint targ = pos; auto &target = pt.target; if( target.first == target.second ) { // Manual target not set, find one automatically const bool u_see = g->u.sees( pos ); int boo_hoo; // @todo calculate chance to hit and cap range based upon this int range = std::min( gun.range(), 12 ); Creature *auto_target = tmp.auto_find_hostile_target( range, boo_hoo, area ); if( auto_target == nullptr ) { if( u_see && boo_hoo ) { add_msg( m_warning, ngettext( "%s points in your direction and emits an IFF warning beep.", "%s points in your direction and emits %d annoyed sounding beeps.", boo_hoo ), tmp.name.c_str(), boo_hoo ); } return 0; } targ = auto_target->pos(); } else if( target.first != target.second ) { // Target set manually // Make sure we didn't move between aiming and firing (it's a bug if we did) if( targ != target.first ) { target.second = target.first; return 0; } targ = target.second; // Remove the target target.second = target.first; } else { // Shouldn't happen target.first = target.second; return 0; } auto shots = gun.fire( tmp, targ ); if( g->u.sees( pos ) && shots ) { add_msg( _( "The %1$s fires its %2$s!" ), name.c_str(), pt.name().c_str() ); } return shots; }