bool overmapbuffer::reveal_route( const tripoint &source, const tripoint &dest, int radius ) { static const int RADIUS = 4; // Maximal radius of search (in overmaps) static const int OX = RADIUS * OMAPX; // half-width of the area to search in static const int OY = RADIUS * OMAPY; // half-height of the area to search in const tripoint start( OX, OY, source.z ); // Local source - center of the local area const tripoint base( source - start ); // To convert local coordinates to global ones const tripoint finish( dest - base ); // Local destination - relative to source const auto estimate = [ this, &base, &finish ]( const pf::node &, const pf::node &cur ) { int res = 0; int omx = base.x + cur.x; int omy = base.y + cur.y; const auto &oter = get_om_global( omx, omy ).get_ter( omx, omy, base.z ); if( !is_ot_type( "road", oter ) && !is_ot_type ( "bridge", oter ) && !is_ot_type( "hiway", oter ) ) { if( is_river( oter ) ) { return -1; // Can't walk on water } // Allow going slightly off-road to overcome small obstacles (e.g. craters), // but heavily penalize that to make roads preferable res += 250; } res += std::abs( finish.x - cur.x ) + std::abs( finish.y - cur.y ); return res; }; const auto path = pf::find_path( start, finish, 2*OX, 2*OY, estimate ); if( path.empty() ) { return false; } for( const auto &node : path ) { reveal( base + tripoint( node.x, node.y, base.z ), radius ); } return true; }
int get_rot_since( const int startturn, const int endturn, const tripoint &location ) { // Ensure food doesn't rot in ice labs, where the // temperature is much less than the weather specifies. tripoint const omt_pos = overmapbuffer::ms_to_omt_copy( location ); oter_id const & oter = overmap_buffer.ter( omt_pos ); // TODO: extract this into a property of the overmap terrain if (is_ot_type("ice_lab", oter)) { return 0; } // TODO: maybe have different rotting speed when underground? int ret = 0; for (calendar i(startturn); i.get_turn() < endturn; i += 600) { w_point w = g->weather_gen->get_weather(location, i); ret += std::min(600, endturn - i.get_turn()) * get_hourly_rotpoints_at_temp(w.temperature) / 600; } return ret; }
/** * Retroactively determine weather-related rotting effects. * Applies rot based on the temperatures incurred between a turn range. */ int get_rot_since( const int since, const int endturn, const point &location ) { // Hack: Ensure food doesn't rot in ice labs, where the // temperature is much less than the weather specifies. // http://github.com/CleverRaven/Cataclysm-DDA/issues/9162 // Bug with this hack: Rot is prevented even when it's above // freezing on the ground floor. oter_id oter = overmap_buffer.ter(g->om_global_location()); if (is_ot_type("ice_lab", oter)) { return 0; } int ret = 0; for (calendar i(since); i.get_turn() < endturn; i += 600) { w_point w = g->weatherGen.get_weather(location, i); ret += std::min(600, endturn - i.get_turn()) * get_hourly_rotpoints_at_temp(w.temperature) / 600; } return ret; }
bool mission::is_complete( const int _npc_id ) const { if( status == mission_status::success ) { return true; } auto &u = g->u; switch( type->goal ) { case MGOAL_GO_TO: { const tripoint cur_pos = g->u.global_omt_location(); return ( rl_dist( cur_pos, target ) <= 1 ); } case MGOAL_GO_TO_TYPE: { const auto cur_ter = overmap_buffer.ter( g->u.global_omt_location() ); return is_ot_type( type->target_id.str(), cur_ter ); } case MGOAL_FIND_ITEM_GROUP: { inventory tmp_inv = u.crafting_inventory(); std::vector<item *> items = std::vector<item *>(); tmp_inv.dump( items ); Group_tag grp_type = type->group_id; itype_id container = type->container_id; bool specific_container_required = container.compare( "null" ) != 0; std::map<itype_id, int> matches = std::map<itype_id, int>(); get_all_item_group_matches( items, grp_type, matches, container, itype_id( "null" ), specific_container_required ); int total_match = std::accumulate( matches.begin(), matches.end(), 0, []( const std::size_t previous, const std::pair<const std::string, std::size_t> &p ) { return previous + p.second; } ); if( total_match >= ( type->item_count ) ) { return true; } } return false; case MGOAL_FIND_ITEM: { if( npc_id != -1 && npc_id != _npc_id ) { return false; } inventory tmp_inv = u.crafting_inventory(); // TODO: check for count_by_charges and use appropriate player::has_* function if( !tmp_inv.has_amount( type->item_id, item_count ) ) { return tmp_inv.has_amount( type->item_id, 1 ) && tmp_inv.has_charges( type->item_id, item_count ); } } return true; case MGOAL_FIND_ANY_ITEM: return u.has_mission_item( uid ) && ( npc_id == -1 || npc_id == _npc_id ); case MGOAL_FIND_MONSTER: if( npc_id != -1 && npc_id != _npc_id ) { return false; } return g->get_creature_if( [&]( const Creature & critter ) { const monster *const mon_ptr = dynamic_cast<const monster *>( &critter ); return mon_ptr && mon_ptr->mission_id == uid; } ); case MGOAL_RECRUIT_NPC: { npc *p = g->find_npc( target_npc_id ); return p != nullptr && p->get_attitude() == NPCATT_FOLLOW; } case MGOAL_RECRUIT_NPC_CLASS: { const auto npcs = overmap_buffer.get_npcs_near_player( 100 ); for( auto &npc : npcs ) { if( npc->myclass == recruit_class && npc->get_attitude() == NPCATT_FOLLOW ) { return true; } } return false; } case MGOAL_FIND_NPC: return npc_id == _npc_id; case MGOAL_ASSASSINATE: return step >= 1; case MGOAL_KILL_MONSTER: return step >= 1; case MGOAL_KILL_MONSTER_TYPE: return g->kill_count( monster_type ) >= kill_count_to_reach; case MGOAL_KILL_MONSTER_SPEC: return g->kill_count( monster_species ) >= kill_count_to_reach; case MGOAL_COMPUTER_TOGGLE: return step >= 1; default: return false; } }
void mission_start::place_npc_software(mission *miss) { npc* dev = g->find_npc(miss->npc_id); if (dev == NULL) { debugmsg("Couldn't find NPC! %d", miss->npc_id); return; } g->u.i_add( item("usb_drive", 0) ); add_msg(_("%s gave you a USB drive."), dev->name.c_str()); std::string type = "house"; switch (dev->myclass) { case NC_HACKER: miss->item_id = "software_hacking"; break; case NC_DOCTOR: miss->item_id = "software_medical"; type = "s_pharm"; miss->follow_up = MISSION_GET_ZOMBIE_BLOOD_ANAL; break; case NC_SCIENTIST: miss->item_id = "software_math"; break; default: miss->item_id = "software_useless"; } int dist = 0; point place; if (type == "house") { int city_id = g->cur_om->closest_city( g->om_location() ); place = g->cur_om->random_house_in_city(city_id); // make it global coordinates place.x += g->cur_om->pos().x * OMAPX; place.y += g->cur_om->pos().y * OMAPY; } else { place = overmap_buffer.find_closest(g->om_global_location(), type, dist, false); } miss->target = place; overmap_buffer.reveal(place, 6, g->levz); tinymap compmap; compmap.load_abs(place.x * 2, place.y * 2, g->levz, false); point comppoint; oter_id oter = g->cur_om->ter(place.x, place.y, 0); if( is_ot_type("house", oter) || is_ot_type("s_pharm", oter) || oter == "" ) { std::vector<point> valid; for (int x = 0; x < SEEX * 2; x++) { for (int y = 0; y < SEEY * 2; y++) { if (compmap.ter(x, y) == t_floor && compmap.furn(x, y) == f_null) { bool okay = false; int wall = 0; for (int x2 = x - 1; x2 <= x + 1 && !okay; x2++) { for (int y2 = y - 1; y2 <= y + 1 && !okay; y2++) { if (compmap.furn(x2, y2) == f_bed || compmap.furn(x2, y2) == f_dresser) { okay = true; valid.push_back( point(x, y) ); } if ( compmap.has_flag_ter("WALL", x2, y2) ) { wall++; } } } if ( wall == 5 ) { if ( compmap.is_last_ter_wall( true, x, y, SEEX * 2, SEEY * 2, NORTH ) && compmap.is_last_ter_wall( true, x, y, SEEX * 2, SEEY * 2, SOUTH ) && compmap.is_last_ter_wall( true, x, y, SEEX * 2, SEEY * 2, WEST ) && compmap.is_last_ter_wall( true, x, y, SEEX * 2, SEEY * 2, EAST ) ) { valid.push_back( point(x, y) ); } } } } } if (valid.empty()) { comppoint = point( rng(6, SEEX * 2 - 7), rng(6, SEEY * 2 - 7) ); } else { comppoint = valid[rng(0, valid.size() - 1)]; } } compmap.ter_set(comppoint.x, comppoint.y, t_console); computer *tmpcomp = compmap.add_computer(comppoint.x, comppoint.y, string_format(_("%s's Terminal"), dev->name.c_str()), 0); tmpcomp->mission_id = miss->uid; tmpcomp->add_option(_("Download Software"), COMPACT_DOWNLOAD_SOFTWARE, 0); compmap.save(); }
bool mission::is_complete( const int _npc_id ) const { if( status == mission_status::success ) { return true; } auto &u = g->u; switch( type->goal ) { case MGOAL_GO_TO: { const tripoint cur_pos = g->u.global_omt_location(); return ( rl_dist( cur_pos, target ) <= 1 ); } break; case MGOAL_GO_TO_TYPE: { const auto cur_ter = overmap_buffer.ter( g->u.global_omt_location() ); return is_ot_type( type->target_id.str(), cur_ter ); } break; case MGOAL_FIND_ITEM: { inventory tmp_inv = u.crafting_inventory(); // TODO: check for count_by_charges and use appropriate player::has_* function if (!tmp_inv.has_amount(type->item_id, item_count)) { return tmp_inv.has_amount( type->item_id, 1 ) && tmp_inv.has_charges( type->item_id, item_count ); } if( npc_id != -1 && npc_id != _npc_id ) { return false; } } return true; case MGOAL_FIND_ANY_ITEM: return u.has_mission_item( uid ) && ( npc_id == -1 || npc_id == _npc_id ); case MGOAL_FIND_MONSTER: if( npc_id != -1 && npc_id != _npc_id ) { return false; } for( size_t i = 0; i < g->num_zombies(); i++ ) { if( g->zombie( i ).mission_id == uid ) { return true; } } return false; case MGOAL_RECRUIT_NPC: { npc *p = g->find_npc( target_npc_id ); return p != nullptr && p->attitude == NPCATT_FOLLOW; } case MGOAL_RECRUIT_NPC_CLASS: { const auto npcs = overmap_buffer.get_npcs_near_player( 100 ); for( auto & npc : npcs ) { if( npc->myclass == recruit_class && npc->attitude == NPCATT_FOLLOW ) { return true; } } return false; } case MGOAL_FIND_NPC: return npc_id == _npc_id; case MGOAL_ASSASSINATE: return step >= 1; case MGOAL_KILL_MONSTER: return step >= 1; case MGOAL_KILL_MONSTER_TYPE: return g->kill_count( mtype_id( monster_type ) ) >= monster_kill_goal; case MGOAL_COMPUTER_TOGGLE: return step >= 1; default: return false; } return false; }