void remove_ammo( item *dis_item, player &p ) { for( auto iter = dis_item->contents.begin(); iter != dis_item->contents.end(); ) { if( iter->has_flag( "IRREMOVABLE" ) ) { iter++; continue; } drop_or_handle( *iter, p ); iter = dis_item->contents.erase( iter ); } if( dis_item->has_flag( "NO_UNLOAD" ) ) { return; } if( dis_item->is_gun() && dis_item->ammo_current() != "null" ) { item ammodrop( dis_item->ammo_current(), calendar::turn ); ammodrop.charges = dis_item->charges; drop_or_handle( ammodrop, p ); dis_item->charges = 0; } if( dis_item->is_tool() && dis_item->charges > 0 && dis_item->ammo_type() ) { item ammodrop( default_ammo( dis_item->ammo_type() ), calendar::turn ); ammodrop.charges = dis_item->charges; if( dis_item->ammo_type() == ammotype( "plutonium" ) ) { ammodrop.charges /= PLUTONIUM_CHARGES; } drop_or_handle( ammodrop, p ); dis_item->charges = 0; } }
Item_spawn_data::ItemList Item_group::create(int birthday, RecursionList &rec) const { ItemList result; if (type == G_COLLECTION) { for(prop_list::const_iterator a = items.begin(); a != items.end(); ++a) { if(rng(0, 99) >= (*a)->probability) { continue; } ItemList tmp = (*a)->create(birthday, rec); result.insert(result.end(), tmp.begin(), tmp.end()); } } else if (type == G_DISTRIBUTION) { int p = rng(0, sum_prob - 1); for(prop_list::const_iterator a = items.begin(); a != items.end(); ++a) { p -= (*a)->probability; if (p >= 0) { continue; } ItemList tmp = (*a)->create(birthday, rec); result.insert(result.end(), tmp.begin(), tmp.end()); break; } } if (with_ammo && !result.empty()) { it_gun *maybe_gun = dynamic_cast<it_gun *>(result.front().type); if (maybe_gun != NULL) { item ammo(default_ammo(maybe_gun->ammo), birthday); // TODO: change the spawn lists to contain proper references to containers ammo = ammo.in_its_container(); result.push_back(ammo); } } return result; }
void npc::starting_weapon(game *g) { skill best = best_skill(); int index; switch (best) { case sk_bashing: switch (rng(0, 5)) { case 0: weapon.make(g->itypes[itm_hammer]); break; case 1: weapon.make(g->itypes[itm_wrench]); break; case 2: weapon.make(g->itypes[itm_hammer_sledge]); break; case 3: weapon.make(g->itypes[itm_pipe]); break; case 4: weapon.make(g->itypes[itm_bat]); break; case 5: weapon.make(g->itypes[itm_crowbar]); break; } break; case sk_cutting: switch (rng(0, 5)) { case 0: weapon.make(g->itypes[itm_knife_butcher]); break; case 1: weapon.make(g->itypes[itm_hatchet]); break; case 2: weapon.make(g->itypes[itm_ax]); break; case 3: weapon.make(g->itypes[itm_machete]); break; case 4: weapon.make(g->itypes[itm_knife_combat]); break; case 5: weapon.make(g->itypes[itm_katana]); break; } break; case sk_throw: // TODO: Some throwing weapons... grenades? break; case sk_pistol: index = rng(0, g->mapitems[mi_pistols].size() - 1); weapon.make(g->itypes[(g->mapitems[mi_pistols])[index]]); break; case sk_shotgun: index = rng(0, g->mapitems[mi_shotguns].size() - 1); weapon.make(g->itypes[(g->mapitems[mi_shotguns])[index]]); break; case sk_smg: index = rng(0, g->mapitems[mi_smg].size() - 1); weapon.make(g->itypes[(g->mapitems[mi_smg])[index]]); break; case sk_rifle: index = rng(0, g->mapitems[mi_rifles].size() - 1); weapon.make(g->itypes[(g->mapitems[mi_rifles])[index]]); break; } if (weapon.is_gun()) { it_gun* gun = dynamic_cast<it_gun*>(weapon.type); weapon.charges = gun->clip; weapon.curammo = dynamic_cast<it_ammo*>(g->itypes[default_ammo(gun->ammo)]); } }
Item_spawn_data::ItemList Item_group::create(int birthday, RecursionList &rec) const { ItemList result; if (type == G_COLLECTION) { for( const auto &elem : items ) { if( rng( 0, 99 ) >= ( elem )->probability ) { continue; } ItemList tmp = ( elem )->create( birthday, rec ); result.insert(result.end(), tmp.begin(), tmp.end()); } } else if (type == G_DISTRIBUTION) { int p = rng(0, sum_prob - 1); for( const auto &elem : items ) { p -= ( elem )->probability; if (p >= 0) { continue; } ItemList tmp = ( elem )->create( birthday, rec ); result.insert(result.end(), tmp.begin(), tmp.end()); break; } } for( auto& e : result ) { bool spawn_ammo = rng( 0, 99 ) < with_ammo; bool spawn_mags = rng( 0, 99 ) < with_magazine || spawn_ammo; if( spawn_mags && !e.magazine_integral() && !e.magazine_current() ) { e.contents.emplace_back( e.magazine_default(), e.bday ); } if( spawn_ammo && e.ammo_capacity() > 0 && e.ammo_remaining() == 0 ) { itype_id ammo = default_ammo( e.ammo_type() ); if( e.magazine_current() ) { e.magazine_current()->contents.emplace_back( ammo, e.bday, e.ammo_capacity() ); } else { e.set_curammo( ammo ); e.charges = e.ammo_capacity(); } } } return result; }
Item_spawn_data::ItemList Item_group::create(int birthday, RecursionList &rec) const { ItemList result; if (type == G_COLLECTION) { for( const auto &elem : items ) { if( rng( 0, 99 ) >= ( elem )->probability ) { continue; } ItemList tmp = ( elem )->create( birthday, rec ); result.insert(result.end(), tmp.begin(), tmp.end()); } } else if (type == G_DISTRIBUTION) { int p = rng(0, sum_prob - 1); for( const auto &elem : items ) { p -= ( elem )->probability; if (p >= 0) { continue; } ItemList tmp = ( elem )->create( birthday, rec ); result.insert(result.end(), tmp.begin(), tmp.end()); break; } } if (with_ammo && !result.empty()) { const auto t = result.front().type; if( t->gun ) { const std::string ammoid = default_ammo( t->gun->ammo ); if ( !ammoid.empty() ) { item ammo( ammoid, birthday ); // TODO: change the spawn lists to contain proper references to containers ammo = ammo.in_its_container(); result.push_back( ammo ); } } } return result; }
/** *Works through cached vehicle definitions and creates vehicle objects from them. */ void vehicle_prototype::finalize() { for( auto &vp : vtypes ) { std::unordered_set<point> cargo_spots; vehicle_prototype &proto = vp.second; const vproto_id &id = vp.first; // Calls the default constructor to create an empty vehicle. Calling the constructor with // the type as parameter would make it look up the type in the map and copy the // (non-existing) blueprint. proto.blueprint.reset( new vehicle() ); vehicle &blueprint = *proto.blueprint; blueprint.type = id; blueprint.name = _(proto.name.c_str()); for( auto &pt : proto.parts ) { auto base = item::find_type( pt.part->item ); if( !pt.part.is_valid() ) { debugmsg("unknown vehicle part %s in %s", pt.part.c_str(), id.c_str()); continue; } if( blueprint.install_part( pt.pos.x, pt.pos.y, pt.part ) < 0 ) { debugmsg( "init_vehicles: '%s' part '%s'(%d) can't be installed to %d,%d", blueprint.name.c_str(), pt.part.c_str(), blueprint.parts.size(), pt.pos.x, pt.pos.y ); } if( !base->gun ) { if( pt.with_ammo ) { debugmsg( "init_vehicles: non-turret %s with ammo in %s", pt.part.c_str(), id.c_str() ); } if( !pt.ammo_types.empty() ) { debugmsg( "init_vehicles: non-turret %s with ammo_types in %s", pt.part.c_str(), id.c_str() ); } if( pt.ammo_qty.first > 0 || pt.ammo_qty.second > 0 ) { debugmsg( "init_vehicles: non-turret %s with ammo_qty in %s", pt.part.c_str(), id.c_str() ); } } else { for( const auto &e : pt.ammo_types ) { auto ammo = item::find_type( e ); if( !ammo->ammo && ammo->ammo->type.count( base->gun->ammo ) ) { debugmsg( "init_vehicles: turret %s has invalid ammo_type %s in %s", pt.part.c_str(), e.c_str(), id.c_str() ); } } if( pt.ammo_types.empty() ) { pt.ammo_types.insert( default_ammo( base->gun->ammo ) ); } } if( base->container ) { if( !item::type_is_defined( pt.fuel ) ) { debugmsg( "init_vehicles: tank %s specified invalid fuel in %s", pt.part.c_str(), id.c_str() ); } } else { if( pt.fuel != "null" ) { debugmsg( "init_vehicles: non-tank %s with fuel in %s", pt.part.c_str(), id.c_str() ); } } if( pt.part.obj().has_flag("CARGO") ) { cargo_spots.insert( pt.pos ); } } for (auto &i : proto.item_spawns) { if( cargo_spots.count( i.pos ) == 0 ) { debugmsg("Invalid spawn location (no CARGO vpart) in %s (%d, %d): %d%%", proto.name.c_str(), i.pos.x, i.pos.y, i.chance); } for (auto &j : i.item_ids) { if( !item::type_is_defined( j ) ) { debugmsg("unknown item %s in spawn list of %s", j.c_str(), id.c_str()); } } for (auto &j : i.item_groups) { if (!item_group::group_is_defined(j)) { debugmsg("unknown item group %s in spawn list of %s", j.c_str(), id.c_str()); } } } } }
std::vector<item> starting_inv(npc *me, npc_class type, game *g) { int total_space = me->volume_capacity() - 2; std::vector<item> ret; itype_id tmp; // First, if we're wielding a gun, get some ammo for it if (me->weapon.is_gun()) { it_gun *gun = dynamic_cast<it_gun*>(me->weapon.type); tmp = default_ammo(gun->ammo); if (total_space >= g->itypes[tmp]->volume) { ret.push_back(item(g->itypes[tmp], 0)); total_space -= ret[ret.size() - 1].volume(); } while ((type == NC_COWBOY || type == NC_BOUNTY_HUNTER || !one_in(3)) && !one_in(4) && total_space >= g->itypes[tmp]->volume) { ret.push_back(item(g->itypes[tmp], 0)); total_space -= ret[ret.size() - 1].volume(); } } if (type == NC_TRADER) { // Traders just have tons of random junk while (total_space > 0 && !one_in(50)) { tmp = itype_id(rng(2, num_items - 1)); // Make sure the item *isn't* in the list of not-okay items // TODO: Make this more efficient for (int i = 0; i < g->mapitems[mi_trader_avoid].size(); i++) { if (tmp == g->mapitems[mi_trader_avoid][i]) { tmp = itype_id(rng(2, num_items - 1)); i = 0; } } if (total_space >= g->itypes[tmp]->volume) { ret.push_back(item(g->itypes[tmp], 0)); ret[ret.size() - 1] = ret[ret.size() - 1].in_its_container(&g->itypes); total_space -= ret[ret.size() - 1].volume(); } } return ret; } int index; items_location from; if (type == NC_DOCTOR) { while(total_space > 0 && !one_in(10)) { if (one_in(3)) from = mi_softdrugs; else from = mi_harddrugs; index = rng(0, g->mapitems[from].size() - 1); tmp = g->mapitems[from][index]; if (total_space >= g->itypes[tmp]->volume) { ret.push_back(item(g->itypes[tmp], 0)); ret[ret.size() - 1] = ret[ret.size() - 1].in_its_container(&g->itypes); total_space -= ret[ret.size() - 1].volume(); } } } // TODO: More specifics. while (total_space > 0 && !one_in(8)) { tmp = itype_id(rng(2, num_items - 1)); if (total_space >= g->itypes[tmp]->volume) { ret.push_back(item(g->itypes[tmp], 0)); ret[ret.size() - 1] = ret[ret.size() - 1].in_its_container(&g->itypes); total_space -= ret[ret.size() - 1].volume(); } } return ret; }
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.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() && qty > 0 ) { new_item.ammo_set( default_ammo( 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() ) { new_item.ammo_set( default_ammo( new_item.ammo_type() ), ch ); } } else { // 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"} const item am = ammo->create_single( new_item.bday ); new_item.ammo_set( am.typeId(), ch > 0 ? ch : 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()); } }
std::vector<item> starting_inv(npc *me, npc_class type, game *g) { int total_space = me->volume_capacity() - 2; std::vector<item> ret; ret.push_back( item(g->itypes[itm_lighter], 0) ); itype_id tmp; // First, if we're wielding a gun, get some ammo for it if (me->weapon.is_gun()) { it_gun *gun = dynamic_cast<it_gun*>(me->weapon.type); tmp = default_ammo(gun->ammo); if (total_space >= g->itypes[tmp]->volume) { ret.push_back(item(g->itypes[tmp], 0)); total_space -= ret[ret.size() - 1].volume(); } while ((type == NC_COWBOY || type == NC_BOUNTY_HUNTER || !one_in(3)) && !one_in(4) && total_space >= g->itypes[tmp]->volume) { ret.push_back(item(g->itypes[tmp], 0)); total_space -= ret[ret.size() - 1].volume(); } } if (type == NC_TRADER) { // Traders just have tons of random junk while (total_space > 0 && !one_in(50)) { tmp = itype_id(rng(2, num_items - 1)); if (total_space >= g->itypes[tmp]->volume) { ret.push_back(item(g->itypes[tmp], 0)); ret[ret.size() - 1] = ret[ret.size() - 1].in_its_container(&g->itypes); total_space -= ret[ret.size() - 1].volume(); } } } int index; items_location from; if (type == NC_HACKER) { from = mi_npc_hacker; while(total_space > 0 && !one_in(10)) { index = rng(0, g->mapitems[from].size() - 1); tmp = g->mapitems[from][index]; item tmpit(g->itypes[tmp], 0); tmpit = tmpit.in_its_container(&g->itypes); if (total_space >= tmpit.volume()) { ret.push_back(tmpit); total_space -= tmpit.volume(); } } } if (type == NC_DOCTOR) { while(total_space > 0 && !one_in(10)) { if (one_in(3)) from = mi_softdrugs; else from = mi_harddrugs; index = rng(0, g->mapitems[from].size() - 1); tmp = g->mapitems[from][index]; item tmpit(g->itypes[tmp], 0); tmpit = tmpit.in_its_container(&g->itypes); if (total_space >= tmpit.volume()) { ret.push_back(tmpit); total_space -= tmpit.volume(); } } } // TODO: More specifics. while (total_space > 0 && !one_in(8)) { tmp = itype_id(rng(4, num_items - 1)); if (total_space >= g->itypes[tmp]->volume) { ret.push_back(item(g->itypes[tmp], 0)); ret[ret.size() - 1] = ret[ret.size() - 1].in_its_container(&g->itypes); total_space -= ret[ret.size() - 1].volume(); } } for (int i = 0; i < ret.size(); i++) { for (int j = 0; j < g->mapitems[mi_trader_avoid].size(); j++) { if (ret[i].type->id == g->mapitems[mi_trader_avoid][j]) { ret.erase(ret.begin() + i); i--; } } } return ret; }
bool game::dump_stats( const std::string& what, dump_mode mode, const std::vector<std::string> &opts ) { try { load_core_data(); } catch( const std::exception &err ) { std::cerr << "Error loading data from json: " << err.what() << std::endl; return false; } DynamicDataLoader::get_instance().finalize_loaded_data(); std::vector<std::string> header; std::vector<std::vector<std::string>> rows; int scol = 0; // sorting column std::map<std::string, standard_npc> test_npcs; test_npcs[ "S1" ] = standard_npc( "S1", { "gloves_survivor", "mask_lsurvivor" }, 4, 8, 10, 8, 10 /* DEX 10, PER 10 */ ); test_npcs[ "S2" ] = standard_npc( "S2", { "gloves_fingerless", "sunglasses" }, 4, 8, 8, 8, 10 /* PER 10 */ ); test_npcs[ "S3" ] = standard_npc( "S3", { "gloves_plate", "helmet_plate" }, 4, 10, 8, 8, 8 /* STAT 10 */ ); test_npcs[ "S4" ] = standard_npc( "S4", {}, 0, 8, 10, 8, 10 /* DEX 10, PER 10 */ ); test_npcs[ "S5" ] = standard_npc( "S5", {}, 4, 8, 10, 8, 10 /* DEX 10, PER 10 */ ); test_npcs[ "S6" ] = standard_npc( "S6", { "gloves_hsurvivor", "mask_hsurvivor" }, 4, 8, 10, 8, 10 /* DEX 10, PER 10 */ ); std::map<std::string, item> test_items; test_items[ "G1" ] = item( "glock_19" ).ammo_set( "9mm" ); test_items[ "G2" ] = item( "hk_mp5" ).ammo_set( "9mm" ); test_items[ "G3" ] = item( "ar15" ).ammo_set( "223" ); test_items[ "G4" ] = item( "remington_700" ).ammo_set( "270" ); test_items[ "G4" ].emplace_back( "rifle_scope" ); if( what == "AMMO" ) { header = { "Name", "Ammo", "Volume", "Weight", "Stack", "Range", "Dispersion", "Recoil", "Damage", "Pierce" }; auto dump = [&rows]( const item& obj ) { // a common task is comparing ammo by type so ammo has multiple repeat the entry for( const auto &e : obj.type->ammo->type ) { std::vector<std::string> r; r.push_back( obj.tname( 1, false ) ); r.push_back( e.str() ); r.push_back( to_string( obj.volume() / units::legacy_volume_factor ) ); r.push_back( to_string( obj.weight() ) ); r.push_back( to_string( obj.type->stack_size ) ); r.push_back( to_string( obj.type->ammo->range ) ); r.push_back( to_string( obj.type->ammo->dispersion ) ); r.push_back( to_string( obj.type->ammo->recoil ) ); r.push_back( to_string( obj.type->ammo->damage ) ); r.push_back( to_string( obj.type->ammo->pierce ) ); rows.push_back( r ); } }; for( auto& e : item_controller->get_all_itypes() ) { if( e.second.ammo ) { dump( item( e.first, calendar::turn, item::solitary_tag {} ) ); } } } else if( what == "ARMOR" ) { header = { "Name", "Encumber (fit)", "Warmth", "Weight", "Storage", "Coverage", "Bash", "Cut", "Acid", "Fire" }; auto dump = [&rows]( const item& obj ) { std::vector<std::string> r; r.push_back( obj.tname( 1, false ) ); r.push_back( to_string( obj.get_encumber() ) ); r.push_back( to_string( obj.get_warmth() ) ); r.push_back( to_string( obj.weight() ) ); r.push_back( to_string( obj.get_storage() / units::legacy_volume_factor ) ); r.push_back( to_string( obj.get_coverage() ) ); r.push_back( to_string( obj.bash_resist() ) ); r.push_back( to_string( obj.cut_resist() ) ); r.push_back( to_string( obj.acid_resist() ) ); r.push_back( to_string( obj.fire_resist() ) ); rows.push_back( r ); }; body_part bp = opts.empty() ? num_bp : get_body_part_token( opts.front() ); for( auto& e : item_controller->get_all_itypes() ) { if( e.second.armor ) { item obj( e.first ); if( bp == num_bp || obj.covers( bp ) ) { if( obj.has_flag( "VARSIZE" ) ) { obj.item_tags.insert( "FIT" ); } dump( obj ); } } } } else if( what == "EDIBLE" ) { header = { "Name", "Volume", "Weight", "Stack", "Calories", "Quench", "Healthy" }; for( const auto& v : vitamin::all() ) { header.push_back( v.second.name() ); } auto dump = [&rows,&test_npcs]( const item& obj ) { std::vector<std::string> r; r.push_back( obj.tname( false ) ); r.push_back( to_string( obj.volume() / units::legacy_volume_factor ) ); r.push_back( to_string( obj.weight() ) ); r.push_back( to_string( obj.type->stack_size ) ); r.push_back( to_string( obj.type->comestible->get_calories() ) ); r.push_back( to_string( obj.type->comestible->quench ) ); r.push_back( to_string( obj.type->comestible->healthy ) ); auto vits = g->u.vitamins_from( obj ); for( const auto& v : vitamin::all() ) { r.push_back( to_string( vits[ v.first ] ) ); } rows.push_back( r ); }; for( auto& e : item_controller->get_all_itypes() ) { if( e.second.comestible && ( e.second.comestible->comesttype == "FOOD" || e.second.comestible->comesttype == "DRINK" ) ) { item food( e.first, calendar::turn, item::solitary_tag {} ); if( g->u.can_eat( food, false, true ) == EDIBLE ) { dump( food ); } } } } else if( what == "GUN" ) { header = { "Name", "Ammo", "Volume", "Weight", "Capacity", "Range", "Dispersion", "Effective recoil", "Damage", "Pierce", "Aim time", "Effective range", "Snapshot range", "Max range" }; std::set<std::string> locations; for( const auto& e : item_controller->get_all_itypes() ) { if( e.second.gun ) { std::transform( e.second.gun->valid_mod_locations.begin(), e.second.gun->valid_mod_locations.end(), std::inserter( locations, locations.begin() ), []( const std::pair<std::string, int>& e ) { return e.first; } ); } } for( const auto &e : locations ) { header.push_back( e ); } auto dump = [&rows,&locations]( const standard_npc &who, const item& obj ) { std::vector<std::string> r; r.push_back( obj.tname( 1, false ) ); r.push_back( obj.ammo_type() ? obj.ammo_type().str() : "" ); r.push_back( to_string( obj.volume() / units::legacy_volume_factor ) ); r.push_back( to_string( obj.weight() ) ); r.push_back( to_string( obj.ammo_capacity() ) ); r.push_back( to_string( obj.gun_range() ) ); r.push_back( to_string( obj.gun_dispersion() ) ); r.push_back( to_string( obj.gun_recoil( who ) ) ); r.push_back( to_string( obj.gun_damage() ) ); r.push_back( to_string( obj.gun_pierce() ) ); r.push_back( to_string( who.gun_engagement_moves( obj ) ) ); r.push_back( string_format( "%.1f", who.gun_engagement_range( obj, player::engagement::effective ) ) ); r.push_back( string_format( "%.1f", who.gun_engagement_range( obj, player::engagement::snapshot ) ) ); r.push_back( string_format( "%.1f", who.gun_engagement_range( obj, player::engagement::maximum ) ) ); for( const auto &e : locations ) { r.push_back( to_string( obj.type->gun->valid_mod_locations[ e ] ) ); } rows.push_back( r ); }; for( const auto& e : item_controller->get_all_itypes() ) { if( e.second.gun ) { item gun( e.first ); if( !gun.magazine_integral() ) { gun.emplace_back( gun.magazine_default() ); } gun.ammo_set( default_ammo( gun.ammo_type() ), gun.ammo_capacity() ); dump( test_npcs[ "S1" ], gun ); if( gun.type->gun->barrel_length > 0 ) { gun.emplace_back( "barrel_small" ); dump( test_npcs[ "S1" ], gun ); } } } } else if( what == "VEHICLE" ) { header = { "Name", "Weight (empty)", "Weight (fueled)", "Max velocity (mph)", "Safe velocity (mph)", "Acceleration (mph/turn)", "Mass coeff %", "Aerodynamics coeff %", "Friction coeff %", "Traction coeff % (grass)" }; auto dump = [&rows]( const vproto_id& obj ) { auto veh_empty = vehicle( obj, 0, 0 ); auto veh_fueled = vehicle( obj, 100, 0 ); std::vector<std::string> r; r.push_back( veh_empty.name ); r.push_back( to_string( veh_empty.total_mass() ) ); r.push_back( to_string( veh_fueled.total_mass() ) ); r.push_back( to_string( veh_fueled.max_velocity() / 100 ) ); r.push_back( to_string( veh_fueled.safe_velocity() / 100 ) ); r.push_back( to_string( veh_fueled.acceleration() / 100 ) ); r.push_back( to_string( (int)( 100 * veh_fueled.k_mass() ) ) ); r.push_back( to_string( (int)( 100 * veh_fueled.k_aerodynamics() ) ) ); r.push_back( to_string( (int)( 100 * veh_fueled.k_friction() ) ) ); r.push_back( to_string( (int)( 100 * veh_fueled.k_traction( veh_fueled.wheel_area( false ) / 2.0f ) ) ) ); rows.push_back( r ); }; for( auto& e : vehicle_prototype::get_all() ) { dump( e ); } } else if( what == "VPART" ) { header = { "Name", "Location", "Weight", "Size" }; auto dump = [&rows]( const vpart_info &obj ) { std::vector<std::string> r; r.push_back( obj.name() ); r.push_back( obj.location ); r.push_back( to_string( int( ceil( item( obj.item ).weight() / 1000.0 ) ) ) ); r.push_back( to_string( obj.size / units::legacy_volume_factor ) ); rows.push_back( r ); }; for( const auto &e : vpart_info::all() ) { dump( e.second ); } } else if( what == "AIMING" ) { scol = -1; // unsorted output so graph columns have predictable ordering const int cycles = 1400; header = { "Name" }; for( int i = 0; i <= cycles; ++i ) { header.push_back( to_string( i ) ); } auto dump = [&rows]( const standard_npc &who, const item &gun) { std::vector<std::string> r( 1, string_format( "%s %s", who.get_name().c_str(), gun.tname().c_str() ) ); double penalty = MIN_RECOIL; for( int i = 0; i <= cycles; ++i ) { penalty -= who.aim_per_move( gun, penalty ); r.push_back( string_format( "%.2f", who.gun_current_range( gun, penalty ) ) ); } rows.push_back( r ); }; if( opts.empty() ) { dump( test_npcs[ "S1" ], test_items[ "G1" ] ); dump( test_npcs[ "S1" ], test_items[ "G2" ] ); dump( test_npcs[ "S1" ], test_items[ "G3" ] ); dump( test_npcs[ "S1" ], test_items[ "G4" ] ); } else { for( const auto &str : opts ) { auto idx = str.find( ':' ); if( idx == std::string::npos ) { std::cerr << "cannot parse test case: " << str << std::endl; return false; } auto test = std::make_pair( test_npcs.find( str.substr( 0, idx ) ), test_items.find( str.substr( idx + 1 ) ) ); if( test.first == test_npcs.end() || test.second == test_items.end() ) { std::cerr << "invalid test case: " << str << std::endl; return false; } dump( test.first->second, test.second->second ); } } } else if( what == "EXPLOSIVE" ) { header = { // @todo Should display more useful data: shrapnel damage, safe range "Name", "Power", "Power at 5 tiles", "Power halves at", "Shrapnel count", "Shrapnel mass" }; auto dump = [&rows]( const std::string &name, const explosion_data &ex ) { std::vector<std::string> r; r.push_back( name ); r.push_back( to_string( ex.power ) ); r.push_back( string_format( "%.1f", ex.power_at_range( 5.0f ) ) ); r.push_back( string_format( "%.1f", ex.expected_range( 0.5f ) ) ); r.push_back( to_string( ex.shrapnel.count ) ); r.push_back( to_string( ex.shrapnel.mass ) ); rows.push_back( r ); }; for( const auto& e : item_controller->get_all_itypes() ) { const auto &itt = e.second; const auto use = itt.get_use( "explosion" ); if( use != nullptr && use->get_actor_ptr() != nullptr ) { const auto actor = dynamic_cast<const explosion_iuse *>( use->get_actor_ptr() ); if( actor != nullptr ) { dump( itt.nname( 1 ), actor->explosion ); } } auto c_ex = dynamic_cast<const explosion_iuse *>( itt.countdown_action.get_actor_ptr() ); if( c_ex != nullptr ) { dump( itt.nname( 1 ), c_ex->explosion ); } } } else { std::cerr << "unknown argument: " << what << std::endl; return false; } rows.erase( std::remove_if( rows.begin(), rows.end(), []( const std::vector<std::string>& e ) { return e.empty(); } ), rows.end() ); if( scol >= 0 ) { std::sort( rows.begin(), rows.end(), [&scol]( const std::vector<std::string>& lhs, const std::vector<std::string>& rhs ) { return lhs[ scol ] < rhs[ scol ]; } ); } rows.erase( std::unique( rows.begin(), rows.end() ), rows.end() ); switch( mode ) { case dump_mode::TSV: rows.insert( rows.begin(), header ); for( const auto& r : rows ) { std::copy( r.begin(), r.end() - 1, std::ostream_iterator<std::string>( std::cout, "\t" ) ); std::cout << r.back() << "\n"; } break; case dump_mode::HTML: std::cout << "<table>"; std::cout << "<thead>"; std::cout << "<tr>"; for( const auto& col : header ) { std::cout << "<th>" << col << "</th>"; } std::cout << "</tr>"; std::cout << "</thead>"; std::cout << "<tdata>"; for( const auto& r : rows ) { std::cout << "<tr>"; for( const auto& col : r ) { std::cout << "<td>" << col << "</td>"; } std::cout << "</tr>"; } std::cout << "</tdata>"; std::cout << "</table>"; break; } return true; }
/** * Called when the activity timer for installing parts, repairing, etc times * out and the the action is complete. */ void complete_vehicle (game *g) { if (g->u.activity.values.size() < 8) { debugmsg ("Invalid activity ACT_VEHICLE values:%d", g->u.activity.values.size()); return; } vehicle *veh = g->m.veh_at (g->u.activity.values[0], g->u.activity.values[1]); if (!veh) { debugmsg ("Activity ACT_VEHICLE: vehicle not found"); return; } char cmd = (char) g->u.activity.index; int dx = g->u.activity.values[4]; int dy = g->u.activity.values[5]; int vehicle_part = g->u.activity.values[6]; int type = g->u.activity.values[7]; std::string part_id = g->u.activity.str_values[0]; std::vector<component> tools; int welder_charges = static_cast<it_tool *>(itypes["welder"])->charges_per_use; int welder_crude_charges = static_cast<it_tool *>(itypes["welder_crude"])->charges_per_use; int partnum; item used_item; bool broken; int replaced_wheel; std::vector<int> parts; int dd = 2; switch (cmd) { case 'i': partnum = veh->install_part (dx, dy, part_id); if(partnum < 0) { debugmsg ("complete_vehicle install part fails dx=%d dy=%d id=%d", dx, dy, part_id.c_str()); } used_item = consume_vpart_item (g, part_id); veh->get_part_properties_from_item(g, partnum, used_item); //transfer damage, etc. tools.push_back(component("welder", welder_charges)); tools.push_back(component("welder_crude", welder_crude_charges)); tools.push_back(component("duct_tape", DUCT_TAPE_USED)); tools.push_back(component("toolset", welder_charges / 20)); g->consume_tools(&g->u, tools, true); if ( vehicle_part_types[part_id].has_flag("CONE_LIGHT") ) { // Need map-relative coordinates to compare to output of look_around. int gx, gy; // Need to call coord_translate() directly since it's a new part. veh->coord_translate(dx, dy, gx, gy); // Stash offset and set it to the location of the part so look_around will start there. int px = g->u.view_offset_x; int py = g->u.view_offset_y; g->u.view_offset_x = veh->global_x() + gx - g->u.posx; g->u.view_offset_y = veh->global_y() + gy - g->u.posy; popup(_("Choose a facing direction for the new headlight.")); point headlight_target = g->look_around(); // Restore previous view offsets. g->u.view_offset_x = px; g->u.view_offset_y = py; int delta_x = headlight_target.x - (veh->global_x() + gx); int delta_y = headlight_target.y - (veh->global_y() + gy); const double PI = 3.14159265358979f; int dir = int(atan2(static_cast<float>(delta_y), static_cast<float>(delta_x)) * 180.0 / PI); dir -= veh->face.dir(); while(dir < 0) { dir += 360; } while(dir > 360) { dir -= 360; } veh->parts[partnum].direction = dir; } g->add_msg (_("You install a %s into the %s."), vehicle_part_types[part_id].name.c_str(), veh->name.c_str()); g->u.practice (g->turn, "mechanics", vehicle_part_types[part_id].difficulty * 5 + 20); break; case 'r': if (veh->parts[vehicle_part].hp <= 0) { veh->break_part_into_pieces(vehicle_part, g->u.posx, g->u.posy); used_item = consume_vpart_item (g, veh->parts[vehicle_part].id); veh->parts[vehicle_part].bigness = used_item.bigness; tools.push_back(component("wrench", -1)); g->consume_tools(&g->u, tools, true); tools.clear(); dd = 0; veh->insides_dirty = true; } tools.push_back(component("welder", welder_charges)); tools.push_back(component("welder_crude", welder_crude_charges)); tools.push_back(component("duct_tape", DUCT_TAPE_USED)); tools.push_back(component("toolset", welder_charges / 20)); g->consume_tools(&g->u, tools, true); veh->parts[vehicle_part].hp = veh->part_info(vehicle_part).durability; g->add_msg (_("You repair the %s's %s."), veh->name.c_str(), veh->part_info(vehicle_part).name.c_str()); g->u.practice (g->turn, "mechanics", (veh->part_info(vehicle_part).difficulty + dd) * 5 + 20); break; case 'f': if (!g->pl_refill_vehicle(*veh, vehicle_part, true)) { debugmsg ("complete_vehicle refill broken"); } g->pl_refill_vehicle(*veh, vehicle_part); break; case 'o': // Dump contents of part at player's feet, if any. for (int i = 0; i < veh->parts[vehicle_part].items.size(); i++) { g->m.add_item_or_charges (g->u.posx, g->u.posy, veh->parts[vehicle_part].items[i]); } veh->parts[vehicle_part].items.clear(); broken = veh->parts[vehicle_part].hp <= 0; if (!broken) { used_item = veh->item_from_part( vehicle_part ); // Transfer fuel back to tank if (used_item.typeId() == "metal_tank") { ammotype desired_liquid = veh->part_info(vehicle_part).fuel_type; item liquid( itypes[default_ammo(desired_liquid)], g->turn ); liquid.charges = veh->parts[vehicle_part].amount; veh->parts[vehicle_part].amount = 0; used_item.put_in(liquid); } g->m.add_item_or_charges(g->u.posx, g->u.posy, used_item); if(type != SEL_JACK) { // Changing tires won't make you a car mechanic g->u.practice (g->turn, "mechanics", 2 * 5 + 20); } } else { veh->break_part_into_pieces(vehicle_part, g->u.posx, g->u.posy); } if (veh->parts.size() < 2) { g->add_msg (_("You completely dismantle the %s."), veh->name.c_str()); g->u.activity.type = ACT_NULL; g->m.destroy_vehicle (veh); } else { if (broken) { g->add_msg(_("You remove the broken %s from the %s."), veh->part_info(vehicle_part).name.c_str(), veh->name.c_str()); } else { g->add_msg(_("You remove the %s from the %s."), veh->part_info(vehicle_part).name.c_str(), veh->name.c_str()); } veh->remove_part (vehicle_part); } break; case 's': g->u.siphon( g, veh, "gasoline" ); break; case 'c': parts = veh->parts_at_relative( dx, dy ); if( parts.size() ) { item removed_wheel; replaced_wheel = veh->part_with_feature( parts[0], "WHEEL", false ); if( replaced_wheel == -1 ) { debugmsg( "no wheel to remove when changing wheels." ); return; } broken = veh->parts[replaced_wheel].hp <= 0; removed_wheel = veh->item_from_part( replaced_wheel ); veh->remove_part( replaced_wheel ); g->add_msg( _("You replace one of the %s's tires with a %s."), veh->name.c_str(), vehicle_part_types[part_id].name.c_str() ); partnum = veh->install_part( dx, dy, part_id ); if( partnum < 0 ) { debugmsg ("complete_vehicle tire change fails dx=%d dy=%d id=%d", dx, dy, part_id.c_str()); } used_item = consume_vpart_item( g, part_id ); veh->get_part_properties_from_item( g, partnum, used_item ); //transfer damage, etc. // Place the removed wheel on the map last so consume_vpart_item() doesn't pick it. if ( !broken ) { g->m.add_item_or_charges( g->u.posx, g->u.posy, removed_wheel ); } } break; case 'd': g->u.siphon( g, veh, "water" ); break; } }
bool game::dump_stats( const std::string& what, dump_mode mode, const std::vector<std::string> &opts ) { try { load_core_data(); load_packs( _( "Loading content packs" ), { "dda" } ); DynamicDataLoader::get_instance().finalize_loaded_data(); } catch( const std::exception &err ) { std::cerr << "Error loading data from json: " << err.what() << std::endl; return false; } std::vector<std::string> header; std::vector<std::vector<std::string>> rows; int scol = 0; // sorting column std::map<std::string, standard_npc> test_npcs; test_npcs[ "S1" ] = standard_npc( "S1", { "gloves_survivor", "mask_lsurvivor" }, 4, 8, 10, 8, 10 /* DEX 10, PER 10 */ ); test_npcs[ "S2" ] = standard_npc( "S2", { "gloves_fingerless", "sunglasses" }, 4, 8, 8, 8, 10 /* PER 10 */ ); test_npcs[ "S3" ] = standard_npc( "S3", { "gloves_plate", "helmet_plate" }, 4, 10, 8, 8, 8 /* STAT 10 */ ); test_npcs[ "S4" ] = standard_npc( "S4", {}, 0, 8, 10, 8, 10 /* DEX 10, PER 10 */ ); test_npcs[ "S5" ] = standard_npc( "S5", {}, 4, 8, 10, 8, 10 /* DEX 10, PER 10 */ ); test_npcs[ "S6" ] = standard_npc( "S6", { "gloves_hsurvivor", "mask_hsurvivor" }, 4, 8, 10, 8, 10 /* DEX 10, PER 10 */ ); std::map<std::string, item> test_items; test_items[ "G1" ] = item( "glock_19" ).ammo_set( "9mm" ); test_items[ "G2" ] = item( "hk_mp5" ).ammo_set( "9mm" ); test_items[ "G3" ] = item( "ar15" ).ammo_set( "223" ); test_items[ "G4" ] = item( "remington_700" ).ammo_set( "270" ); test_items[ "G4" ].emplace_back( "rifle_scope" ); if( what == "AMMO" ) { header = { "Name", "Ammo", "Volume", "Weight", "Stack", "Range", "Dispersion", "Recoil", "Damage", "Pierce" }; auto dump = [&rows]( const item& obj ) { // a common task is comparing ammo by type so ammo has multiple repeat the entry for( const auto &e : obj.type->ammo->type ) { std::vector<std::string> r; r.push_back( obj.tname( 1, false ) ); r.push_back( e.str() ); r.push_back( to_string( obj.volume() / units::legacy_volume_factor ) ); r.push_back( to_string( to_gram( obj.weight() ) ) ); r.push_back( to_string( obj.type->stack_size ) ); r.push_back( to_string( obj.type->ammo->range ) ); r.push_back( to_string( obj.type->ammo->dispersion ) ); r.push_back( to_string( obj.type->ammo->recoil ) ); r.push_back( to_string( obj.type->ammo->damage ) ); r.push_back( to_string( obj.type->ammo->pierce ) ); rows.push_back( r ); } }; for( const itype *e : item_controller->all() ) { if( e->ammo ) { dump( item( e, calendar::turn, item::solitary_tag {} ) ); } } } else if( what == "ARMOR" ) { header = { "Name", "Encumber (fit)", "Warmth", "Weight", "Storage", "Coverage", "Bash", "Cut", "Acid", "Fire" }; auto dump = [&rows]( const item& obj ) { std::vector<std::string> r; r.push_back( obj.tname( 1, false ) ); r.push_back( to_string( obj.get_encumber() ) ); r.push_back( to_string( obj.get_warmth() ) ); r.push_back( to_string( to_gram( obj.weight() ) ) ); r.push_back( to_string( obj.get_storage() / units::legacy_volume_factor ) ); r.push_back( to_string( obj.get_coverage() ) ); r.push_back( to_string( obj.bash_resist() ) ); r.push_back( to_string( obj.cut_resist() ) ); r.push_back( to_string( obj.acid_resist() ) ); r.push_back( to_string( obj.fire_resist() ) ); rows.push_back( r ); }; body_part bp = opts.empty() ? num_bp : get_body_part_token( opts.front() ); for( const itype *e : item_controller->all() ) { if( e->armor ) { item obj( e ); if( bp == num_bp || obj.covers( bp ) ) { if( obj.has_flag( "VARSIZE" ) ) { obj.item_tags.insert( "FIT" ); } dump( obj ); } } } } else if( what == "EDIBLE" ) { header = { "Name", "Volume", "Weight", "Stack", "Calories", "Quench", "Healthy" }; for( const auto& v : vitamin::all() ) { header.push_back( v.second.name() ); } auto dump = [&rows]( const item& obj ) { std::vector<std::string> r; r.push_back( obj.tname( false ) ); r.push_back( to_string( obj.volume() / units::legacy_volume_factor ) ); r.push_back( to_string( to_gram( obj.weight() ) ) ); r.push_back( to_string( obj.type->stack_size ) ); r.push_back( to_string( obj.type->comestible->get_calories() ) ); r.push_back( to_string( obj.type->comestible->quench ) ); r.push_back( to_string( obj.type->comestible->healthy ) ); auto vits = g->u.vitamins_from( obj ); for( const auto& v : vitamin::all() ) { r.push_back( to_string( vits[ v.first ] ) ); } rows.push_back( r ); }; for( const itype *e : item_controller->all() ) { item food( e, calendar::turn, item::solitary_tag {} ); if( food.is_food() && g->u.can_eat( food ) == EDIBLE ) { dump( food ); } } } else if( what == "GUN" ) { header = { "Name", "Ammo", "Volume", "Weight", "Capacity", "Range", "Dispersion", "Effective recoil", "Damage", "Pierce", "Aim time", "Effective range", "Snapshot range", "Max range" }; std::set<std::string> locations; for( const itype *e : item_controller->all() ) { if( e->gun ) { std::transform( e->gun->valid_mod_locations.begin(), e->gun->valid_mod_locations.end(), std::inserter( locations, locations.begin() ), []( const std::pair<gunmod_location, int>& q ) { return q.first.name(); } ); } } for( const auto &e : locations ) { header.push_back( e ); } auto dump = [&rows,&locations]( const standard_npc &who, const item& obj ) { std::vector<std::string> r; r.push_back( obj.tname( 1, false ) ); r.push_back( obj.ammo_type() ? obj.ammo_type().str() : "" ); r.push_back( to_string( obj.volume() / units::legacy_volume_factor ) ); r.push_back( to_string( to_gram( obj.weight() ) ) ); r.push_back( to_string( obj.ammo_capacity() ) ); r.push_back( to_string( obj.gun_range() ) ); r.push_back( to_string( obj.gun_dispersion() ) ); r.push_back( to_string( obj.gun_recoil( who ) ) ); r.push_back( to_string( obj.gun_damage() ) ); r.push_back( to_string( obj.gun_pierce() ) ); r.push_back( to_string( who.gun_engagement_moves( obj ) ) ); for( const auto &e : locations ) { r.push_back( to_string( obj.type->gun->valid_mod_locations[ e ] ) ); } rows.push_back( r ); }; for( const itype *e : item_controller->all() ) { if( e->gun ) { item gun( e ); if( !gun.magazine_integral() ) { gun.emplace_back( gun.magazine_default() ); } gun.ammo_set( default_ammo( gun.ammo_type() ), gun.ammo_capacity() ); dump( test_npcs[ "S1" ], gun ); if( gun.type->gun->barrel_length > 0 ) { gun.emplace_back( "barrel_small" ); dump( test_npcs[ "S1" ], gun ); } } } } else if( what == "RECIPE" ) { // optionally filter recipes to include only those using specified skills recipe_subset dict; for( const auto &r : recipe_dict ) { if( opts.empty() || std::any_of( opts.begin(), opts.end(), [&r]( const std::string &s ) { if( r.second.skill_used == skill_id( s ) && r.second.difficulty > 0 ) { return true; } auto iter = r.second.required_skills.find( skill_id( s ) ); return iter != r.second.required_skills.end() && iter->second > 0; } ) ) { dict.include( &r.second ); } } // only consider skills that are required by at least one recipe std::vector<Skill> sk; std::copy_if( Skill::skills.begin(), Skill::skills.end(), std::back_inserter( sk ), [&dict]( const Skill &s ) { return std::any_of( dict.begin(), dict.end(), [&s]( const recipe *r ) { return r->skill_used == s.ident() || r->required_skills.find( s.ident() ) != r->required_skills.end(); } ); } ); header = { "Result" }; for( const auto &e : sk ) { header.push_back( e.ident().str() ); } for( const recipe *e : dict ) { std::vector<std::string> r; r.push_back( item::find_type( e->result )->nname( 1 ) ); for( const auto &s : sk ) { if( e->skill_used == s.ident() ) { r.push_back( to_string( e->difficulty ) ); } else { auto iter = e->required_skills.find( s.ident() ); r.push_back( to_string( iter != e->required_skills.end() ? iter->second : 0 ) ); } } rows.push_back( r ); } } else if( what == "VEHICLE" ) { header = { "Name", "Weight (empty)", "Weight (fueled)", "Max velocity (mph)", "Safe velocity (mph)", "Acceleration (mph/turn)", "Mass coeff %", "Aerodynamics coeff %", "Friction coeff %", "Traction coeff % (grass)" }; auto dump = [&rows]( const vproto_id& obj ) { auto veh_empty = vehicle( obj, 0, 0 ); auto veh_fueled = vehicle( obj, 100, 0 ); std::vector<std::string> r; r.push_back( veh_empty.name ); r.push_back( to_string( to_kilogram( veh_empty.total_mass() ) ) ); r.push_back( to_string( to_kilogram( veh_fueled.total_mass() ) ) ); r.push_back( to_string( veh_fueled.max_velocity() / 100 ) ); r.push_back( to_string( veh_fueled.safe_velocity() / 100 ) ); r.push_back( to_string( veh_fueled.acceleration() / 100 ) ); r.push_back( to_string( (int)( 100 * veh_fueled.k_mass() ) ) ); r.push_back( to_string( (int)( 100 * veh_fueled.k_aerodynamics() ) ) ); r.push_back( to_string( (int)( 100 * veh_fueled.k_friction() ) ) ); r.push_back( to_string( (int)( 100 * veh_fueled.k_traction( veh_fueled.wheel_area( false ) / 2.0f ) ) ) ); rows.push_back( r ); }; for( auto& e : vehicle_prototype::get_all() ) { dump( e ); } } else if( what == "VPART" ) { header = { "Name", "Location", "Weight", "Size" }; auto dump = [&rows]( const vpart_info &obj ) { std::vector<std::string> r; r.push_back( obj.name() ); r.push_back( obj.location ); r.push_back( to_string( int( ceil( to_gram( item( obj.item ).weight() ) / 1000.0 ) ) ) ); r.push_back( to_string( obj.size / units::legacy_volume_factor ) ); rows.push_back( r ); }; for( const auto &e : vpart_info::all() ) { dump( e.second ); } } else if( what == "EXPLOSIVE" ) { header = { // @todo Should display more useful data: shrapnel damage, safe range "Name", "Power", "Power at 5 tiles", "Power halves at", "Shrapnel count", "Shrapnel mass" }; auto dump = [&rows]( const std::string &name, const explosion_data &ex ) { std::vector<std::string> r; r.push_back( name ); r.push_back( to_string( ex.power ) ); r.push_back( string_format( "%.1f", ex.power_at_range( 5.0f ) ) ); r.push_back( string_format( "%.1f", ex.expected_range( 0.5f ) ) ); r.push_back( to_string( ex.shrapnel.count ) ); r.push_back( to_string( ex.shrapnel.mass ) ); rows.push_back( r ); }; for( const itype *e : item_controller->all() ) { const auto use = e->get_use( "explosion" ); if( use != nullptr && use->get_actor_ptr() != nullptr ) { const auto actor = dynamic_cast<const explosion_iuse *>( use->get_actor_ptr() ); if( actor != nullptr ) { dump( e->nname( 1 ), actor->explosion ); } } auto c_ex = dynamic_cast<const explosion_iuse *>( e->countdown_action.get_actor_ptr() ); if( c_ex != nullptr ) { dump( e->nname( 1 ), c_ex->explosion ); } } } else { std::cerr << "unknown argument: " << what << std::endl; return false; } rows.erase( std::remove_if( rows.begin(), rows.end(), []( const std::vector<std::string>& e ) { return e.empty(); } ), rows.end() ); if( scol >= 0 ) { std::sort( rows.begin(), rows.end(), [&scol]( const std::vector<std::string>& lhs, const std::vector<std::string>& rhs ) { return lhs[ scol ] < rhs[ scol ]; } ); } rows.erase( std::unique( rows.begin(), rows.end() ), rows.end() ); switch( mode ) { case dump_mode::TSV: rows.insert( rows.begin(), header ); for( const auto& r : rows ) { std::copy( r.begin(), r.end() - 1, std::ostream_iterator<std::string>( std::cout, "\t" ) ); std::cout << r.back() << "\n"; } break; case dump_mode::HTML: std::cout << "<table>"; std::cout << "<thead>"; std::cout << "<tr>"; for( const auto& col : header ) { std::cout << "<th>" << col << "</th>"; } std::cout << "</tr>"; std::cout << "</thead>"; std::cout << "<tdata>"; for( const auto& r : rows ) { std::cout << "<tr>"; for( const auto& col : r ) { std::cout << "<td>" << col << "</td>"; } std::cout << "</tr>"; } std::cout << "</tdata>"; std::cout << "</table>"; break; } return true; }
// Handles interactions with a vehicle in the examine menu. // Returns the part number that wil accept items if any, or -1 to indicate no cargo part. // Returns -2 if a special interaction was performed and the menu should exit. int Pickup::interact_with_vehicle( vehicle *veh, int posx, int posy, int veh_root_part ) { bool from_vehicle = false; int k_part = 0; int wtr_part = 0; int w_part = 0; int craft_part = 0; int cargo_part = 0; int chempart = 0; int ctrl_part = 0; std::vector<std::string> menu_items; std::vector<uimenu_entry> options_message; std::vector<item> here_ground = g->m.i_at(posx, posy); if( veh ) { k_part = veh->part_with_feature(veh_root_part, "KITCHEN"); wtr_part = veh->part_with_feature(veh_root_part, "FAUCET"); w_part = veh->part_with_feature(veh_root_part, "WELDRIG"); craft_part = veh->part_with_feature(veh_root_part, "CRAFTRIG"); chempart = veh->part_with_feature(veh_root_part, "CHEMLAB"); cargo_part = veh->part_with_feature(veh_root_part, "CARGO", false); ctrl_part = veh->part_with_feature(veh_root_part, "CONTROLS"); from_vehicle = veh && cargo_part >= 0 && !veh->parts[cargo_part].items.empty(); menu_items.push_back(_("Examine vehicle")); options_message.push_back(uimenu_entry(_("Examine vehicle"), 'e')); if (ctrl_part >= 0) { menu_items.push_back(_("Control vehicle")); options_message.push_back(uimenu_entry(_("Control vehicle"), 'v')); } if( from_vehicle ) { menu_items.push_back(_("Get items")); options_message.push_back(uimenu_entry(_("Get items"), 'g')); } if(!here_ground.empty()) { menu_items.push_back(_("Get items on the ground")); options_message.push_back(uimenu_entry(_("Get items on the ground"), 'i')); } if((k_part >= 0 || chempart >= 0) && veh->fuel_left("battery") > 0) { menu_items.push_back(_("Use the hotplate")); options_message.push_back(uimenu_entry(_("Use the hotplate"), 'h')); } if((k_part >= 0 || wtr_part >= 0) && veh->fuel_left("water") > 0) { menu_items.push_back(_("Fill a container with water")); options_message.push_back(uimenu_entry(_("Fill a container with water"), 'c')); menu_items.push_back(_("Have a drink")); options_message.push_back(uimenu_entry(_("Have a drink"), 'd')); } if(w_part >= 0 && veh->fuel_left("battery") > 0) { menu_items.push_back(_("Use the welding rig?")); options_message.push_back(uimenu_entry(_("Use the welding rig?"), 'w')); } if(craft_part >= 0 && veh->fuel_left("battery") > 0) { menu_items.push_back(_("Use the water purifier?")); options_message.push_back(uimenu_entry(_("Use the water purifier?"), 'p')); } int choice; if( menu_items.size() == 1 ) { choice = 0; } else { uimenu selectmenu; selectmenu.return_invalid = true; selectmenu.text = _("Select an action"); selectmenu.entries = options_message; selectmenu.selected = 0; selectmenu.query(); choice = selectmenu.ret; } if(choice < 0) { return -2; } if(menu_items[choice] == _("Use the hotplate")) { //Will be -1 if no battery at all item tmp_hotplate( "hotplate", 0 ); // Drain a ton of power tmp_hotplate.charges = veh->drain( "battery", 100 ); if( tmp_hotplate.is_tool() ) { it_tool *tmptool = dynamic_cast<it_tool *>((&tmp_hotplate)->type); if ( tmp_hotplate.charges >= tmptool->charges_per_use ) { tmptool->invoke(&g->u, &tmp_hotplate, false); tmp_hotplate.charges -= tmptool->charges_per_use; veh->refill( "battery", tmp_hotplate.charges ); } } return -2; } if(menu_items[choice] == _("Fill a container with water")) { int amt = veh->drain("water", veh->fuel_left("water")); item fill_water( default_ammo("water"), calendar::turn ); fill_water.charges = amt; int back = g->move_liquid(fill_water); if (back >= 0) { veh->refill("water", back); } else { veh->refill("water", amt); } return -2; } if(menu_items[choice] == _("Have a drink")) { veh->drain("water", 1); item water( "water_clean", 0 ); g->u.eat(&water, dynamic_cast<it_comest *>(water.type)); g->u.moves -= 250; return -2; } if(menu_items[choice] == _("Use the welding rig?")) { //Will be -1 if no battery at all item tmp_welder( "welder", 0 ); // Drain a ton of power tmp_welder.charges = veh->drain( "battery", 1000 ); if( tmp_welder.is_tool() ) { it_tool *tmptool = dynamic_cast<it_tool *>((&tmp_welder)->type); if ( tmp_welder.charges >= tmptool->charges_per_use ) { tmptool->invoke( &g->u, &tmp_welder, false ); tmp_welder.charges -= tmptool->charges_per_use; veh->refill( "battery", tmp_welder.charges ); } } return -2; } if(menu_items[choice] == _("Use the water purifier?")) { //Will be -1 if no battery at all item tmp_purifier( "water_purifier", 0 ); // Drain a ton of power tmp_purifier.charges = veh->drain( "battery", 100 ); if( tmp_purifier.is_tool() ) { it_tool *tmptool = dynamic_cast<it_tool *>((&tmp_purifier)->type); if ( tmp_purifier.charges >= tmptool->charges_per_use ) { tmptool->invoke( &g->u, &tmp_purifier, false ); tmp_purifier.charges -= tmptool->charges_per_use; veh->refill( "battery", tmp_purifier.charges ); } } return -2; } if(menu_items[choice] == _("Control vehicle")) { veh->use_controls(); return -2; } if(menu_items[choice] == _("Examine vehicle")) { g->exam_vehicle(*veh, posx, posy); return -2; } if(menu_items[choice] == _("Get items on the ground")) { from_vehicle = false; } } return from_vehicle ? cargo_part : -1; }
void game::dump_stats( const std::string& what, dump_mode mode ) { load_core_data(); DynamicDataLoader::get_instance().finalize_loaded_data(); std::vector<std::string> header; std::vector<std::vector<std::string>> rows; if( what == "AMMO" ) { header = { "Name", "Ammo", "Volume", "Weight", "Stack", "Range", "Dispersion", "Recoil", "Damage", "Pierce" }; auto dump = [&rows]( const item& obj ) { std::vector<std::string> r; r.push_back( obj.tname( false ) ); r.push_back( obj.type->ammo->type ); r.push_back( std::to_string( obj.volume() ) ); r.push_back( std::to_string( obj.weight() ) ); r.push_back( std::to_string( obj.type->stack_size ) ); r.push_back( std::to_string( obj.type->ammo->range ) ); r.push_back( std::to_string( obj.type->ammo->dispersion ) ); r.push_back( std::to_string( obj.type->ammo->recoil ) ); r.push_back( std::to_string( obj.type->ammo->damage ) ); r.push_back( std::to_string( obj.type->ammo->pierce ) ); rows.push_back( r ); }; for( auto& e : item_controller->get_all_itypes() ) { if( e.second->ammo ) { dump( item( e.first, calendar::turn, item::solitary_tag {} ) ); } } } else if( what == "EDIBLE" ) { header = { "Name", "Volume", "Weight", "Stack", "Calories", "Quench", "Healthy" }; for( const auto& v : vitamin::all() ) { header.push_back( v.second.name() ); } auto dump = [&rows]( const item& obj ) { std::vector<std::string> r; r.push_back( obj.tname( false ) ); r.push_back( std::to_string( obj.volume() ) ); r.push_back( std::to_string( obj.weight() ) ); r.push_back( std::to_string( obj.type->stack_size ) ); r.push_back( std::to_string( obj.type->comestible->get_calories() ) ); r.push_back( std::to_string( obj.type->comestible->quench ) ); r.push_back( std::to_string( obj.type->comestible->healthy ) ); auto vits = g->u.vitamins_from( obj ); for( const auto& v : vitamin::all() ) { r.push_back( std::to_string( vits[ v.first ] ) ); } rows.push_back( r ); }; for( auto& e : item_controller->get_all_itypes() ) { if( e.second->comestible && ( e.second->comestible->comesttype == "FOOD" || e.second->comestible->comesttype == "DRINK" ) ) { item food( e.first, calendar::turn, item::solitary_tag {} ); if( g->u.can_eat( food, false, true ) == EDIBLE ) { dump( food ); } } } } else if( what == "GUN" ) { header = { "Name", "Ammo", "Volume", "Weight", "Capacity", "Range", "Dispersion", "Recoil", "Damage", "Pierce" }; std::set<std::string> locations; for( const auto& e : item_controller->get_all_itypes() ) { if( e.second->gun ) { std::transform( e.second->gun->valid_mod_locations.begin(), e.second->gun->valid_mod_locations.end(), std::inserter( locations, locations.begin() ), []( const std::pair<std::string, int>& e ) { return e.first; } ); } } for( const auto &e : locations ) { header.push_back( e ); } auto dump = [&rows,&locations]( const item& obj ) { std::vector<std::string> r; r.push_back( obj.tname( false ) ); r.push_back( obj.ammo_type() != "NULL" ? obj.ammo_type() : "" ); r.push_back( std::to_string( obj.volume() ) ); r.push_back( std::to_string( obj.weight() ) ); r.push_back( std::to_string( obj.ammo_capacity() ) ); r.push_back( std::to_string( obj.gun_range() ) ); r.push_back( std::to_string( obj.gun_dispersion() ) ); r.push_back( std::to_string( obj.gun_recoil() ) ); r.push_back( std::to_string( obj.gun_damage() ) ); r.push_back( std::to_string( obj.gun_pierce() ) ); for( const auto &e : locations ) { r.push_back( std::to_string( obj.type->gun->valid_mod_locations[ e ] ) ); } rows.push_back( r ); }; for( const auto& e : item_controller->get_all_itypes() ) { if( e.second->gun ) { item gun( e.first ); if( gun.is_reloadable() ) { gun.ammo_set( default_ammo( gun.ammo_type() ), gun.ammo_capacity() ); } dump( gun ); if( gun.type->gun->barrel_length > 0 ) { gun.emplace_back( "barrel_small" ); dump( gun ); } } } } else if( what == "VEHICLE" ) { header = { "Name", "Weight (empty)", "Weight (fueled)" }; auto dump = [&rows]( const vproto_id& obj ) { auto veh_empty = vehicle( obj, 0, 0 ); auto veh_fueled = vehicle( obj, 100, 0 ); std::vector<std::string> r; r.push_back( veh_empty.name ); r.push_back( std::to_string( veh_empty.total_mass() ) ); r.push_back( std::to_string( veh_fueled.total_mass() ) ); rows.push_back( r ); }; for( auto& e : vehicle_prototype::get_all() ) { dump( e ); } } else if( what == "VPART" ) { header = { "Name", "Location", "Weight", "Size" }; auto dump = [&rows]( const vpart_info *obj ) { std::vector<std::string> r; r.push_back( obj->name() ); r.push_back( obj->location ); r.push_back( std::to_string( int( ceil( item( obj->item ).weight() / 1000.0 ) ) ) ); r.push_back( std::to_string( obj->size ) ); rows.push_back( r ); }; for( const auto e : vpart_info::get_all() ) { dump( e ); } } rows.erase( std::remove_if( rows.begin(), rows.end(), []( const std::vector<std::string>& e ) { return e.empty(); } ), rows.end() ); std::sort( rows.begin(), rows.end(), []( const std::vector<std::string>& lhs, const std::vector<std::string>& rhs ) { return lhs[ 0 ] < rhs[ 0 ]; } ); rows.erase( std::unique( rows.begin(), rows.end() ), rows.end() ); switch( mode ) { case dump_mode::TSV: rows.insert( rows.begin(), header ); for( const auto& r : rows ) { std::copy( r.begin(), r.end() - 1, std::ostream_iterator<std::string>( std::cout, "\t" ) ); std::cout << r.back() << "\n"; } break; case dump_mode::HTML: std::cout << "<table>"; std::cout << "<thead>"; std::cout << "<tr>"; for( const auto& col : header ) { std::cout << "<th>" << col << "</th>"; } std::cout << "</tr>"; std::cout << "</thead>"; std::cout << "<tdata>"; for( const auto& r : rows ) { std::cout << "<tr>"; for( const auto& col : r ) { std::cout << "<td>" << col << "</td>"; } std::cout << "</tr>"; } std::cout << "</tdata>"; std::cout << "</table>"; break; } }