int player::get_acquirable_energy( const item &it, rechargeable_cbm cbm ) const { switch( cbm ) { case rechargeable_cbm::none: break; case rechargeable_cbm::battery: return std::min<long>( it.charges, std::numeric_limits<int>::max() ); case rechargeable_cbm::reactor: if( it.charges > 0 ) { const auto iter = plut_charges.find( it.typeId() ); return iter != plut_charges.end() ? it.charges * iter->second : 0; } break; case rechargeable_cbm::furnace: { int amount = ( it.volume() / 250_ml + it.weight() / 1_gram ) / 9; // @todo JSONize. if( it.made_of( material_id( "leather" ) ) ) { amount /= 4; } if( it.made_of( material_id( "wood" ) ) ) { amount /= 2; } return amount; } } return 0; }
void drop_or_handle( const item &newit, player &p ) { if( newit.made_of( LIQUID ) && &p == &g->u ) { // TODO: what about NPCs? g->handle_all_liquid( newit, PICKUP_RANGE ); } else { item tmp( newit ); p.i_add_or_drop( tmp ); } }
bool vehicle_part::can_reload( const item &obj ) const { // first check part is not destroyed and can contain ammo if( !is_fuel_store() ) { return false; } if( !obj.is_null() ) { const itype_id obj_type = obj.typeId(); if( is_reactor() ) { return base.is_reloadable_with( obj_type ); } // forbid filling tanks with solids or non-material things if( is_tank() && ( obj.made_of( SOLID ) || obj.made_of( PNULL ) ) ) { return false; } // forbid putting liquids, gasses, and plasma in things that aren't tanks else if( !obj.made_of( SOLID ) && !is_tank() ) { return false; } // prevent mixing of different ammo if( ammo_current() != "null" && ammo_current() != obj_type ) { return false; } // For storage with set type, prevent filling with different types if( info().fuel_type != fuel_type_none && info().fuel_type != obj_type ) { return false; } // don't fill magazines with inappropriate fuel if( !is_tank() && !base.is_reloadable_with( obj_type ) ) { return false; } } return ammo_remaining() < ammo_capacity(); }
void Item_modifier::modify(item &new_item) const { if(new_item.is_null()) { return; } int dm = (damage.first == damage.second) ? damage.first : rng(damage.first, damage.second); if(dm >= -1 && dm <= 4) { new_item.damage = dm; } long ch = (charges.first == charges.second) ? charges.first : rng(charges.first, charges.second); if(ch != -1) { it_tool *t = dynamic_cast<it_tool *>(new_item.type); it_gun *g = dynamic_cast<it_gun *>(new_item.type); if(new_item.count_by_charges()) { // food, ammo new_item.charges = ch; } else if(t != NULL) { new_item.charges = std::min(ch, t->max_charges); } else if(g != NULL && ammo.get() != NULL) { item am = ammo->create_single(new_item.bday); it_ammo *a = dynamic_cast<it_ammo *>(am.type); if(!am.is_null() && a != NULL) { new_item.curammo = a; new_item.charges = std::min<long>(am.charges, new_item.clip_size()); } } } if(container.get() != NULL) { item cont = container->create_single(new_item.bday); if (!cont.is_null()) { if (new_item.made_of(LIQUID)) { LIQUID_FILL_ERROR err; int rc = cont.get_remaining_capacity_for_liquid(new_item, err); if(rc > 0 && (new_item.charges > rc || ch == -1)) { // make sure the container is not over-full. // fill up the container (if using default charges) new_item.charges = rc; } } cont.put_in(new_item); new_item = cont; } } if (contents.get() != NULL) { Item_spawn_data::ItemList contentitems = contents->create(new_item.bday); new_item.contents.insert(new_item.contents.end(), contentitems.begin(), contentitems.end()); } }
void set_item_inventory( item &newit ) { if( newit.made_of( LIQUID ) ) { g->handle_all_liquid( newit, PICKUP_RANGE ); } else { g->u.inv.assign_empty_invlet( newit ); // We might not have space for the item if( !g->u.can_pickVolume( newit ) ) { //Accounts for result_mult add_msg( _( "There's no room in your inventory for the %s, so you drop it." ), newit.tname().c_str() ); g->m.add_item_or_charges( g->u.pos(), newit ); } else if( !g->u.can_pickWeight( newit, !get_option<bool>( "DANGEROUS_PICKUPS" ) ) ) { add_msg( _( "The %s is too heavy to carry, so you drop it." ), newit.tname().c_str() ); g->m.add_item_or_charges( g->u.pos(), newit ); } else { newit = g->u.i_add( newit ); add_msg( m_info, "%c - %s", newit.invlet == 0 ? ' ' : newit.invlet, newit.tname().c_str() ); } } }
void Item_modifier::modify(item &new_item) const { if(new_item.is_null()) { return; } new_item.damage = std::min( std::max( (int) rng( damage.first, damage.second ), MIN_ITEM_DAMAGE ), MAX_ITEM_DAMAGE ); long ch = (charges.first == charges.second) ? charges.first : rng(charges.first, charges.second); if(ch != -1) { if( new_item.count_by_charges() || new_item.made_of( LIQUID ) ) { // food, ammo // count_by_charges requires that charges is at least 1. It makes no sense to // spawn a "water (0)" item. new_item.charges = std::max( 1l, ch ); } else if( new_item.is_tool() ) { const auto qty = std::min( ch, new_item.ammo_capacity() ); new_item.charges = qty; if( new_item.ammo_type() != "NULL" && qty > 0 ) { new_item.ammo_set( new_item.ammo_type(), qty ); } } else if( !new_item.is_gun() ) { //not gun, food, ammo or tool. new_item.charges = ch; } } if( new_item.is_gun() && ( ammo.get() != nullptr || ch > 0 ) ) { if( ammo.get() == nullptr ) { // In case there is no explicit ammo item defined, use the default ammo if( new_item.ammo_type() != "NULL" ) { new_item.charges = ch; new_item.set_curammo( new_item.ammo_type() ); } } else { const item am = ammo->create_single( new_item.bday ); new_item.set_curammo( am ); // Prefer explicit charges of the gun, else take the charges of the ammo item, // Gun charges are easier to define: {"item":"gun","charge":10,"ammo-item":"ammo"} if( ch > 0 ) { new_item.charges = ch; } else { new_item.charges = am.charges; } } // Make sure the item is in valid state if( new_item.ammo_data() && new_item.magazine_integral() ) { new_item.charges = std::min( new_item.charges, new_item.ammo_capacity() ); } else { new_item.charges = 0; } } if(container.get() != NULL) { item cont = container->create_single(new_item.bday); if (!cont.is_null()) { if (new_item.made_of(LIQUID)) { long rc = cont.get_remaining_capacity_for_liquid(new_item); if(rc > 0 && (new_item.charges > rc || ch == -1)) { // make sure the container is not over-full. // fill up the container (if using default charges) new_item.charges = rc; } } cont.put_in(new_item); new_item = cont; } } if (contents.get() != NULL) { Item_spawn_data::ItemList contentitems = contents->create(new_item.bday); new_item.contents.insert(new_item.contents.end(), contentitems.begin(), contentitems.end()); } }
void Pickup::pick_one_up( const tripoint &pickup_target, item &newit, vehicle *veh, int cargo_part, int index, int quantity, bool &got_water, bool &offered_swap, PickupMap &mapPickup, bool autopickup ) { player &u = g->u; int moves_taken = 100; bool picked_up = false; pickup_answer option = CANCEL; item leftovers = newit; if( newit.invlet != '\0' && u.invlet_to_position( newit.invlet ) != INT_MIN ) { // Existing invlet is not re-usable, remove it and let the code in player.cpp/inventory.cpp // add a new invlet, otherwise keep the (usable) invlet. newit.invlet = '\0'; } if( quantity != 0 && newit.count_by_charges() ) { // Reinserting leftovers happens after item removal to avoid stacking issues. leftovers.charges = newit.charges - quantity; if( leftovers.charges > 0 ) { newit.charges = quantity; } } else { leftovers.charges = 0; } if( newit.made_of( LIQUID ) ) { got_water = true; } else if( !u.can_pickWeight( newit, false ) ) { add_msg( m_info, _( "The %s is too heavy!" ), newit.display_name().c_str() ); } else if( newit.is_ammo() && ( newit.ammo_type() == ammotype( "arrow" ) || newit.ammo_type() == ammotype( "bolt" ) ) ) { // @todo Make quiver code generic so that ammo pouches can use it too //add ammo to quiver int quivered = handle_quiver_insertion( newit, moves_taken, picked_up ); if( quivered > 0 ) { quantity = quivered; //already picked up some for quiver so use special case handling picked_up = true; option = NUM_ANSWERS; } if( newit.charges > 0 ) { if( !u.can_pickVolume( newit ) ) { if( !autopickup ) { // Silence some messaging if we're doing autopickup. add_msg( m_info, ngettext( "There's no room in your inventory for the %s.", "There's no room in your inventory for the %s.", newit.charges ), newit.tname( newit.charges ).c_str() ); } } else { // Add to inventory instead option = STASH; } } if( option == NUM_ANSWERS ) { //not picking up the rest so //update the charges for the item that gets re-added to the game map leftovers.charges = newit.charges; } } else if( newit.is_bucket() && !newit.is_container_empty() ) { if( !autopickup ) { const std::string &explain = string_format( _( "Can't stash %s while it's not empty" ), newit.display_name().c_str() ); option = handle_problematic_pickup( newit, offered_swap, explain ); } else { option = CANCEL; } } else if( !u.can_pickVolume( newit ) ) { if( !autopickup ) { const std::string &explain = string_format( _( "Not enough capacity to stash %s" ), newit.display_name().c_str() ); option = handle_problematic_pickup( newit, offered_swap, explain ); } else { option = CANCEL; } } else { option = STASH; } switch( option ) { case NUM_ANSWERS: // Some other option break; case CANCEL: picked_up = false; break; case WEAR: picked_up = u.wear_item( newit ); break; case WIELD: picked_up = u.wield( newit ); if( !picked_up ) { break; } if( u.weapon.invlet ) { add_msg( m_info, _( "Wielding %c - %s" ), u.weapon.invlet, u.weapon.display_name().c_str() ); } else { add_msg( m_info, _( "Wielding - %s" ), u.weapon.display_name().c_str() ); } break; case SPILL: if( newit.is_container_empty() ) { debugmsg( "Tried to spill contents from an empty container" ); break; } picked_up = newit.spill_contents( u ); if( !picked_up ) { break; } // Intentional fallthrough case STASH: auto &entry = mapPickup[newit.tname()]; entry.second += newit.count_by_charges() ? newit.charges : 1; entry.first = u.i_add( newit ); picked_up = true; break; } if( picked_up ) { Pickup::remove_from_map_or_vehicle( pickup_target, veh, cargo_part, moves_taken, index ); } if( leftovers.charges > 0 ) { bool to_map = veh == nullptr; if( !to_map ) { to_map = !veh->add_item( cargo_part, leftovers ); } if( to_map ) { g->m.add_item_or_charges( pickup_target, leftovers ); } } }
void Pickup::pick_one_up( const tripoint &pickup_target, item &newit, vehicle *veh, int cargo_part, int index, int quantity, bool &got_water, bool &offered_swap, PickupMap &mapPickup, bool autopickup ) { int moves_taken = 100; bool picked_up = false; item leftovers = newit; if( newit.invlet != '\0' && g->u.invlet_to_position( newit.invlet ) != INT_MIN ) { // Existing invlet is not re-usable, remove it and let the code in player.cpp/inventory.cpp // add a new invlet, otherwise keep the (usable) invlet. newit.invlet = '\0'; } if( quantity != 0 && newit.count_by_charges() ) { // Reinserting leftovers happens after item removal to avoid stacking issues. leftovers.charges = newit.charges - quantity; if( leftovers.charges > 0 ) { newit.charges = quantity; } } else { leftovers.charges = 0; } if( newit.made_of(LIQUID) ) { got_water = true; } else if (!g->u.can_pickWeight(newit.weight(), false)) { add_msg(m_info, _("The %s is too heavy!"), newit.display_name().c_str()); } else if( newit.is_ammo() && (newit.ammo_type() == "arrow" || newit.ammo_type() == "bolt")) { //add ammo to quiver int quivered = handle_quiver_insertion( newit, moves_taken, picked_up); if( newit.charges > 0) { if(!g->u.can_pickVolume( newit.volume())) { if(quivered > 0) { //update the charges for the item that gets re-added to the game map quantity = quivered; leftovers.charges = newit.charges; } if( !autopickup ) { // Silence some messaging if we're doing autopickup. add_msg(m_info, ngettext("There's no room in your inventory for the %s.", "There's no room in your inventory for the %s.", newit.charges), newit.tname(newit.charges).c_str()); } } else { //add to inventory instead item &it = g->u.i_add(newit); picked_up = true; //display output message PickupMap map_pickup; int charges = (newit.count_by_charges()) ? newit.charges : 1; map_pickup.insert(std::pair<std::string, ItemCount>(newit.tname(), ItemCount(it, charges))); show_pickup_message(map_pickup); } } } else if (!g->u.can_pickVolume(newit.volume())) { if( !autopickup ) { // Armor can be instantly worn if (newit.is_armor() && query_yn(_("Put on the %s?"), newit.display_name().c_str())) { if (g->u.wear_item(newit)) { picked_up = true; } } else if (g->u.is_armed()) { if (!g->u.weapon.has_flag("NO_UNWIELD")) { if( !offered_swap ) { offered_swap = true; if ( g->u.weapon.type->id != newit.type->id && query_yn(_("No space for %1$s; wield instead? (drops %2$s)"), newit.display_name().c_str(), g->u.weapon.display_name().c_str()) ) { picked_up = true; g->m.add_item_or_charges( pickup_target, g->u.remove_weapon(), 1 ); g->u.inv.assign_empty_invlet( newit, true ); // force getting an invlet. g->u.wield( &( g->u.i_add(newit) ) ); if (newit.invlet) { add_msg(m_info, _("Wielding %c - %s"), newit.invlet, newit.display_name().c_str()); } else { add_msg(m_info, _("Wielding - %s"), newit.display_name().c_str()); } } } } else { add_msg(m_info, _("There's no room in your inventory for the %s " "and you can't unwield your %s."), newit.display_name().c_str(), g->u.weapon.display_name().c_str()); } } else if( !g->u.is_armed() ) { if (g->u.keep_hands_free) { add_msg(m_info, _("There's no room in your inventory for the %s " "and you have decided to keep your hands free."), newit.display_name().c_str()); } else { g->u.inv.assign_empty_invlet(newit, true); // force getting an invlet. g->u.wield(&(g->u.i_add(newit))); picked_up = true; if (newit.invlet) { add_msg(m_info, _("Wielding %c - %s"), newit.invlet, newit.display_name().c_str()); } else { add_msg(m_info, _("Wielding - %s"), newit.display_name().c_str()); } } } // end of if unarmed } // end of if !autopickup } else { auto &entry = mapPickup[newit.tname()]; entry.second += newit.count_by_charges() ? newit.charges : 1; entry.first = g->u.i_add(newit); picked_up = true; } if(picked_up) { Pickup::remove_from_map_or_vehicle(pickup_target, veh, cargo_part, moves_taken, index); } if( leftovers.charges > 0 ) { bool to_map = veh == nullptr; if( !to_map ) { to_map = !veh->add_item( cargo_part, leftovers ); } if( to_map ) { g->m.add_item_or_charges( pickup_target, leftovers ); } } }
void game::throw_item(player &p, int tarx, int tary, item &thrown, std::vector<point> &trajectory) { int deviation = 0; int trange = 1.5 * rl_dist(p.posx, p.posy, tarx, tary); // Throwing attempts below "Basic Competency" level are extra-bad int skillLevel = p.skillLevel("throw"); if (skillLevel < 3) deviation += rng(0, 8 - skillLevel); if (skillLevel < 8) deviation += rng(0, 8 - skillLevel); else deviation -= skillLevel - 6; deviation += p.throw_dex_mod(); if (p.per_cur < 6) deviation += rng(0, 8 - p.per_cur); else if (p.per_cur > 8) deviation -= p.per_cur - 8; deviation += rng(0, p.encumb(bp_hands) * 2 + p.encumb(bp_eyes) + 1); if (thrown.volume() > 5) deviation += rng(0, 1 + (thrown.volume() - 5) / 4); if (thrown.volume() == 0) deviation += rng(0, 3); deviation += rng(0, 1 + abs(p.str_cur - thrown.weight())); double missed_by = .01 * deviation * trange; bool missed = false; int tart; if (missed_by >= 1) { // We missed D: // Shoot a random nearby space? if (missed_by > 9) missed_by = 9; tarx += rng(0 - int(sqrt(double(missed_by))), int(sqrt(double(missed_by)))); tary += rng(0 - int(sqrt(double(missed_by))), int(sqrt(double(missed_by)))); if (m.sees(p.posx, p.posy, tarx, tary, -1, tart)) trajectory = line_to(p.posx, p.posy, tarx, tary, tart); else trajectory = line_to(p.posx, p.posy, tarx, tary, 0); missed = true; if (!p.is_npc()) add_msg("You miss!"); } else if (missed_by >= .6) { // Hit the space, but not necessarily the monster there missed = true; if (!p.is_npc()) add_msg("You barely miss!"); } std::string message; int dam = (thrown.weight() / 4 + thrown.type->melee_dam / 2 + p.str_cur / 2) / double(2 + double(thrown.volume() / 4)); if (dam > thrown.weight() * 3) dam = thrown.weight() * 3; int i = 0, tx = 0, ty = 0; for (i = 0; i < trajectory.size() && dam > -10; i++) { message = ""; double goodhit = missed_by; tx = trajectory[i].x; ty = trajectory[i].y; // If there's a monster in the path of our item, and either our aim was true, // OR it's not the monster we were aiming at and we were lucky enough to hit it if (mon_at(tx, ty) != -1 && (!missed || one_in(7 - int(z[mon_at(tx, ty)].type->size)))) { if (rng(0, 100) < 20 + skillLevel * 12 && thrown.type->melee_cut > 0) { if (!p.is_npc()) { message += " You cut the "; message += z[mon_at(tx, ty)].name(); message += "!"; } if (thrown.type->melee_cut > z[mon_at(tx, ty)].armor_cut()) dam += (thrown.type->melee_cut - z[mon_at(tx, ty)].armor_cut()); } if (thrown.made_of(GLASS) && !thrown.active && // active = molotov, etc. rng(0, thrown.volume() + 8) - rng(0, p.str_cur) < thrown.volume()) { if (u_see(tx, ty)) add_msg("The %s shatters!", thrown.tname().c_str()); for (int i = 0; i < thrown.contents.size(); i++) m.add_item(tx, ty, thrown.contents[i]); sound(tx, ty, 16, "glass breaking!"); int glassdam = rng(0, thrown.volume() * 2); if (glassdam > z[mon_at(tx, ty)].armor_cut()) dam += (glassdam - z[mon_at(tx, ty)].armor_cut()); } else m.add_item(tx, ty, thrown); if (i < trajectory.size() - 1) goodhit = double(double(rand() / RAND_MAX) / 2); if (goodhit < .1 && !z[mon_at(tx, ty)].has_flag(MF_NOHEAD)) { message = "Headshot!"; dam = rng(dam, dam * 3); p.practice(turn, "throw", 5); } else if (goodhit < .2) { message = "Critical!"; dam = rng(dam, dam * 2); p.practice(turn, "throw", 2); } else if (goodhit < .4) dam = rng(int(dam / 2), int(dam * 1.5)); else if (goodhit < .5) { message = "Grazing hit."; dam = rng(0, dam); } if (!p.is_npc()) add_msg("%s You hit the %s for %d damage.", message.c_str(), z[mon_at(tx, ty)].name().c_str(), dam); else if (u_see(tx, ty)) add_msg("%s hits the %s for %d damage.", message.c_str(), z[mon_at(tx, ty)].name().c_str(), dam); if (z[mon_at(tx, ty)].hurt(dam)) kill_mon(mon_at(tx, ty), !p.is_npc()); return; } else // No monster hit, but the terrain might be. m.shoot(this, tx, ty, dam, false, 0); if (m.move_cost(tx, ty) == 0) { if (i > 0) { tx = trajectory[i - 1].x; ty = trajectory[i - 1].y; } else { tx = u.posx; ty = u.posy; } i = trajectory.size(); } } if (m.move_cost(tx, ty) == 0) { if (i > 1) { tx = trajectory[i - 2].x; ty = trajectory[i - 2].y; } else { tx = u.posx; ty = u.posy; } } if (thrown.made_of(GLASS) && !thrown.active && // active means molotov, etc rng(0, thrown.volume() + 8) - rng(0, p.str_cur) < thrown.volume()) { if (u_see(tx, ty)) add_msg("The %s shatters!", thrown.tname().c_str()); for (int i = 0; i < thrown.contents.size(); i++) m.add_item(tx, ty, thrown.contents[i]); sound(tx, ty, 16, "glass breaking!"); } else { sound(tx, ty, 8, "thud."); m.add_item(tx, ty, thrown); } }
void pick_one_up( const tripoint &pickup_target, item &newit, vehicle *veh, int cargo_part, int index, int quantity, bool &got_water, bool &offered_swap, PickupMap &mapPickup, bool autopickup ) { player &u = g->u; int moves_taken = 100; bool picked_up = false; pickup_answer option = CANCEL; item leftovers = newit; if( newit.invlet != '\0' && u.invlet_to_position( newit.invlet ) != INT_MIN ) { // Existing invlet is not re-usable, remove it and let the code in player.cpp/inventory.cpp // add a new invlet, otherwise keep the (usable) invlet. newit.invlet = '\0'; } if( quantity != 0 && newit.count_by_charges() ) { // Reinserting leftovers happens after item removal to avoid stacking issues. leftovers.charges = newit.charges - quantity; if( leftovers.charges > 0 ) { newit.charges = quantity; } } else { leftovers.charges = 0; } if( newit.made_of( LIQUID ) ) { got_water = true; } else if( !u.can_pickWeight( newit, false ) ) { add_msg( m_info, _( "The %s is too heavy!" ), newit.display_name().c_str() ); } else if( newit.is_bucket() && !newit.is_container_empty() ) { if( !autopickup ) { const std::string &explain = string_format( _( "Can't stash %s while it's not empty" ), newit.display_name().c_str() ); option = handle_problematic_pickup( newit, offered_swap, explain ); } else { option = CANCEL; } } else if( !u.can_pickVolume( newit ) ) { if( !autopickup ) { const std::string &explain = string_format( _( "Not enough capacity to stash %s" ), newit.display_name().c_str() ); option = handle_problematic_pickup( newit, offered_swap, explain ); } else { option = CANCEL; } } else { option = STASH; } switch( option ) { case NUM_ANSWERS: // Some other option break; case CANCEL: picked_up = false; break; case WEAR: picked_up = u.wear_item( newit ); break; case WIELD: picked_up = u.wield( newit ); if( !picked_up ) { break; } if( u.weapon.invlet ) { add_msg( m_info, _( "Wielding %c - %s" ), u.weapon.invlet, u.weapon.display_name().c_str() ); } else { add_msg( m_info, _( "Wielding - %s" ), u.weapon.display_name().c_str() ); } break; case SPILL: if( newit.is_container_empty() ) { debugmsg( "Tried to spill contents from an empty container" ); break; } picked_up = newit.spill_contents( u ); if( !picked_up ) { break; } // Intentional fallthrough case STASH: auto &entry = mapPickup[newit.tname()]; entry.second += newit.count_by_charges() ? newit.charges : 1; entry.first = u.i_add( newit ); picked_up = true; break; } if( picked_up ) { remove_from_map_or_vehicle( pickup_target, veh, cargo_part, moves_taken, index ); } if( leftovers.charges > 0 ) { bool to_map = veh == nullptr; if( !to_map ) { to_map = !veh->add_item( cargo_part, leftovers ); } if( to_map ) { g->m.add_item_or_charges( pickup_target, leftovers ); } } }
void Item_modifier::modify(item &new_item) const { if(new_item.is_null()) { return; } new_item.damage = std::min( std::max( (int) rng( damage.first, damage.second ), MIN_ITEM_DAMAGE ), MAX_ITEM_DAMAGE ); long ch = (charges.first == charges.second) ? charges.first : rng(charges.first, charges.second); const auto g = new_item.type->gun.get(); const auto t = dynamic_cast<const it_tool *>(new_item.type); if(ch != -1) { if( new_item.count_by_charges() || new_item.made_of( LIQUID ) ) { // food, ammo // count_by_charges requires that charges is at least 1. It makes no sense to // spawn a "water (0)" item. new_item.charges = std::max( 1l, ch ); } else if(t != NULL) { new_item.charges = std::min(ch, t->max_charges); } else if (g == nullptr){ //not gun, food, ammo or tool. new_item.charges = ch; } } if( g != nullptr && ( ammo.get() != nullptr || ch > 0 ) ) { if( ammo.get() == nullptr ) { // In case there is no explicit ammo item defined, use the default ammo const auto ammoid = default_ammo( g->ammo ); if ( !ammoid.empty() ) { new_item.set_curammo( ammoid ); new_item.charges = ch; } } else { const item am = ammo->create_single( new_item.bday ); new_item.set_curammo( am ); // Prefer explicit charges of the gun, else take the charges of the ammo item, // Gun charges are easier to define: {"item":"gun","charge":10,"ammo-item":"ammo"} if( ch > 0 ) { new_item.charges = ch; } else { new_item.charges = am.charges; } } // Make sure the item is in a valid state curammo==0 <=> charges==0 and respect clip size if( !new_item.has_curammo() ) { new_item.charges = 0; } else { new_item.charges = std::min<long>( new_item.charges, new_item.clip_size() ); } } if(container.get() != NULL) { item cont = container->create_single(new_item.bday); if (!cont.is_null()) { if (new_item.made_of(LIQUID)) { long rc = cont.get_remaining_capacity_for_liquid(new_item); if(rc > 0 && (new_item.charges > rc || ch == -1)) { // make sure the container is not over-full. // fill up the container (if using default charges) new_item.charges = rc; } } cont.put_in(new_item); new_item = cont; } } if (contents.get() != NULL) { Item_spawn_data::ItemList contentitems = contents->create(new_item.bday); new_item.contents.insert(new_item.contents.end(), contentitems.begin(), contentitems.end()); } }
void Item_modifier::modify( item &new_item ) const { if( new_item.is_null() ) { return; } new_item.set_damage( rng( damage.first, damage.second ) ); long ch = ( charges.first == charges.second ) ? charges.first : rng( charges.first, charges.second ); if( ch != -1 ) { if( new_item.count_by_charges() || new_item.made_of( LIQUID ) ) { // food, ammo // count_by_charges requires that charges is at least 1. It makes no sense to // spawn a "water (0)" item. new_item.charges = std::max( 1l, ch ); } else if( new_item.is_tool() ) { const auto qty = std::min( ch, new_item.ammo_capacity() ); new_item.charges = qty; if( new_item.ammo_type() && qty > 0 ) { new_item.ammo_set( new_item.ammo_type()->default_ammotype(), qty ); } } else if( !new_item.is_gun() ) { //not gun, food, ammo or tool. new_item.charges = ch; } } if( ch > 0 && ( new_item.is_gun() || new_item.is_magazine() ) ) { if( ammo == nullptr ) { // In case there is no explicit ammo item defined, use the default ammo if( new_item.ammo_type() ) { new_item.ammo_set( new_item.ammo_type()->default_ammotype(), ch ); } } else { const item am = ammo->create_single( new_item.birthday() ); new_item.ammo_set( am.typeId(), ch ); } // Make sure the item is in valid state if( new_item.ammo_data() && new_item.magazine_integral() ) { new_item.charges = std::min( new_item.charges, new_item.ammo_capacity() ); } else { new_item.charges = 0; } } if( new_item.is_tool() || new_item.is_gun() || new_item.is_magazine() ) { bool spawn_ammo = rng( 0, 99 ) < with_ammo && new_item.ammo_remaining() == 0 && ch == -1 && ( !new_item.is_tool() || new_item.type->tool->rand_charges.empty() ); bool spawn_mag = rng( 0, 99 ) < with_magazine && !new_item.magazine_integral() && !new_item.magazine_current(); if( spawn_mag ) { new_item.contents.emplace_back( new_item.magazine_default(), new_item.birthday() ); } if( spawn_ammo ) { if( ammo ) { const item am = ammo->create_single( new_item.birthday() ); new_item.ammo_set( am.typeId() ); } else { new_item.ammo_set( new_item.ammo_type()->default_ammotype() ); } } } if( container != nullptr ) { item cont = container->create_single( new_item.birthday() ); if( !cont.is_null() ) { if( new_item.made_of( LIQUID ) ) { long rc = cont.get_remaining_capacity_for_liquid( new_item ); if( rc > 0 && ( new_item.charges > rc || ch == -1 ) ) { // make sure the container is not over-full. // fill up the container (if using default charges) new_item.charges = rc; } } cont.put_in( new_item ); new_item = cont; } } if( contents != nullptr ) { Item_spawn_data::ItemList contentitems = contents->create( new_item.birthday() ); new_item.contents.insert( new_item.contents.end(), contentitems.begin(), contentitems.end() ); } for( auto &flag : custom_flags ) { new_item.set_flag( flag ); } }
void game::throw_item(player &p, int tarx, int tary, item &thrown, std::vector<point> &trajectory) { int deviation = 0; int trange = 1.5 * rl_dist(p.posx, p.posy, tarx, tary); std::set<std::string> no_effects; // Throwing attempts below "Basic Competency" level are extra-bad int skillLevel = p.skillLevel("throw"); if (skillLevel < 3) { deviation += rng(0, 8 - skillLevel); } if (skillLevel < 8) { deviation += rng(0, 8 - skillLevel); } else { deviation -= skillLevel - 6; } deviation += p.throw_dex_mod(); if (p.per_cur < 6) { deviation += rng(0, 8 - p.per_cur); } else if (p.per_cur > 8) { deviation -= p.per_cur - 8; } deviation += rng(0, p.encumb(bp_hands) * 2 + p.encumb(bp_eyes) + 1); if (thrown.volume() > 5) { deviation += rng(0, 1 + (thrown.volume() - 5) / 4); } if (thrown.volume() == 0) { deviation += rng(0, 3); } deviation += rng(0, std::max( 0, p.str_cur - thrown.weight() / 113 ) ); double missed_by = .01 * deviation * trange; bool missed = false; int tart; if (missed_by >= 1) { // We missed D: // Shoot a random nearby space? if (missed_by > 9) { missed_by = 9; } tarx += rng(0 - int(sqrt(double(missed_by))), int(sqrt(double(missed_by)))); tary += rng(0 - int(sqrt(double(missed_by))), int(sqrt(double(missed_by)))); if (m.sees(p.posx, p.posy, tarx, tary, -1, tart)) { trajectory = line_to(p.posx, p.posy, tarx, tary, tart); } else { trajectory = line_to(p.posx, p.posy, tarx, tary, 0); } missed = true; add_msg_if_player(&p,_("You miss!")); } else if (missed_by >= .6) { // Hit the space, but not necessarily the monster there missed = true; add_msg_if_player(&p,_("You barely miss!")); } std::string message; int real_dam = (thrown.weight() / 452 + thrown.type->melee_dam / 2 + p.str_cur / 2) / double(2 + double(thrown.volume() / 4)); if (real_dam > thrown.weight() / 40) { real_dam = thrown.weight() / 40; } if (p.has_active_bionic("bio_railgun") && (thrown.made_of("iron") || thrown.made_of("steel"))) { real_dam *= 2; } int dam = real_dam; int i = 0, tx = 0, ty = 0; for (i = 0; i < trajectory.size() && dam >= 0; i++) { message = ""; double goodhit = missed_by; tx = trajectory[i].x; ty = trajectory[i].y; const int zid = mon_at(tx, ty); // If there's a monster in the path of our item, and either our aim was true, // OR it's not the monster we were aiming at and we were lucky enough to hit it if (zid != -1 && (!missed || one_in(7 - int(zombie(zid).type->size)))) { monster &z = zombie(zid); if (rng(0, 100) < 20 + skillLevel * 12 && thrown.type->melee_cut > 0) { if (!p.is_npc()) { message += string_format(_(" You cut the %s!"), z.name().c_str()); } if (thrown.type->melee_cut > z.get_armor_cut(bp_torso)) { dam += (thrown.type->melee_cut - z.get_armor_cut(bp_torso)); } } if (thrown.made_of("glass") && !thrown.active && // active = molotov, etc. rng(0, thrown.volume() + 8) - rng(0, p.str_cur) < thrown.volume()) { if (u_see(tx, ty)) { add_msg(_("The %s shatters!"), thrown.tname().c_str()); } for (int i = 0; i < thrown.contents.size(); i++) { m.add_item_or_charges(tx, ty, thrown.contents[i]); } sound(tx, ty, 16, _("glass breaking!")); int glassdam = rng(0, thrown.volume() * 2); if (glassdam > z.get_armor_cut(bp_torso)) { dam += (glassdam - z.get_armor_cut(bp_torso)); } } else { m.add_item_or_charges(tx, ty, thrown); } if (i < trajectory.size() - 1) { goodhit = double(double(rand() / RAND_MAX) / 2); } if (goodhit < .1 && !z.has_flag(MF_NOHEAD)) { message = _("Headshot!"); dam = rng(dam, dam * 3); p.practice(turn, "throw", 5); p.lifetime_stats()->headshots++; } else if (goodhit < .2) { message = _("Critical!"); dam = rng(dam, dam * 2); p.practice(turn, "throw", 2); } else if (goodhit < .4) { dam = rng(int(dam / 2), int(dam * 1.5)); } else if (goodhit < .5) { message = _("Grazing hit."); dam = rng(0, dam); } if (u_see(tx, ty)) { g->add_msg_player_or_npc(&p, _("%s You hit the %s for %d damage."), _("%s <npcname> hits the %s for %d damage."), message.c_str(), z.name().c_str(), dam); } if (z.hurt(dam, real_dam)) { z.die(&p); } return; } else { // No monster hit, but the terrain might be. m.shoot(tx, ty, dam, false, no_effects); } // Collide with impassable terrain if (m.move_cost(tx, ty) == 0) { if (i > 0) { tx = trajectory[i - 1].x; ty = trajectory[i - 1].y; } else { tx = u.posx; ty = u.posy; } i = trajectory.size(); } if (p.has_active_bionic("bio_railgun") && (thrown.made_of("iron") || thrown.made_of("steel"))) { m.add_field(tx, ty, fd_electricity, rng(2,3)); } } if (thrown.made_of("glass") && !thrown.active && // active means molotov, etc rng(0, thrown.volume() + 8) - rng(0, p.str_cur) < thrown.volume()) { if (u_see(tx, ty)) { add_msg(_("The %s shatters!"), thrown.tname().c_str()); } for (int i = 0; i < thrown.contents.size(); i++) { m.add_item_or_charges(tx, ty, thrown.contents[i]); } sound(tx, ty, 16, _("glass breaking!")); } else { if(m.has_flag("LIQUID", tx, ty)) { sound(tx, ty, 10, _("splash!")); } else { sound(tx, ty, 8, _("thud.")); } m.add_item_or_charges(tx, ty, thrown); } }