void activity_on_turn_move_items() { // Move activity has source square, target square, // indices of items on map, and quantities of same. point source = g->u.activity.placement; point destination = point( g->u.activity.values[0], g->u.activity.values[1] ); std::list<int> indices; std::list<int> quantities; // Note i = 2, skipping first few elements. for( size_t i = 2; i < g->u.activity.values.size(); i += 2 ) { indices.push_back( g->u.activity.values[i] ); quantities.push_back( g->u.activity.values[ i + 1 ] ); } // Nuke the current activity, leaving the backlog alone. g->u.activity = player_activity(); move_items( source, destination, indices, quantities ); if( !indices.empty() ) { g->u.assign_activity( ACT_MOVE_ITEMS, 0 ); g->u.activity.placement = source; g->u.activity.values.push_back( destination.x ); g->u.activity.values.push_back( destination.y ); while( !indices.empty() ) { g->u.activity.values.push_back( indices.front() ); indices.pop_front(); g->u.activity.values.push_back( quantities.front() ); quantities.pop_front(); } } }
void player::disassemble_all( bool one_pass ) { // Reset all the activity values assign_activity( activity_id( "ACT_DISASSEMBLE" ), 0 ); auto items = g->m.i_at( pos() ); bool found_any = false; if( !one_pass ) { // Kinda hacky // That INT_MIN notes we want infinite uncraft // If INT_MIN is reached in complete_disassemble, // we will call this function again. activity.values.push_back( INT_MIN ); activity.str_values.push_back( "" ); activity.coords.push_back( tripoint_min ); } for( size_t i = 0; i < items.size(); i++ ) { if( disassemble( items[i], i, true, false ) ) { found_any = true; } } if( !found_any ) { // Reset the activity - don't loop if there is nothing to do activity = player_activity(); } }
void craft_command::execute() { if( empty() ) { return; } bool need_selections = true; inventory map_inv; map_inv.form_from_map( crafter->pos(), PICKUP_RANGE ); if( has_cached_selections() ) { std::vector<comp_selection<item_comp>> missing_items = check_item_components_missing( map_inv ); std::vector<comp_selection<tool_comp>> missing_tools = check_tool_components_missing( map_inv ); if( missing_items.empty() && missing_tools.empty() ) { // All items we used previously are still there, so we don't need to do selection. need_selections = false; } else if( !query_continue( missing_items, missing_tools ) ) { return; } } const auto needs = rec->requirements(); if( need_selections ) { item_selections.clear(); for( const auto &it : needs.get_components() ) { comp_selection<item_comp> is = crafter->select_item_component( it, batch_size, map_inv, true ); if( is.use_from == cancel ) { return; } item_selections.push_back( is ); } tool_selections.clear(); for( const auto &it : needs.get_tools() ) { comp_selection<tool_comp> ts = crafter->select_tool_component( it, batch_size, map_inv, DEFAULT_HOTKEYS, true ); if( ts.use_from == cancel ) { return; } tool_selections.push_back( ts ); } } auto activity = player_activity( is_long ? ACT_LONGCRAFT : ACT_CRAFT, rec->batch_time( batch_size ), -1, INT_MIN, rec->ident() ); activity.values.push_back( batch_size ); crafter->assign_activity( activity ); /* legacy support for lua bindings to last_batch and lastrecipe */ crafter->last_batch = batch_size; crafter->lastrecipe = rec->ident(); }
/* values explanation * 0: items from vehicle? * 1: items to a vehicle? * 2: index <-+ * 3: amount | * n: ^-------+ */ void activity_on_turn_move_items() { // Drop activity if we don't know destination coordinates. if( g->u.activity.coords.empty() ) { g->u.activity = player_activity(); return; } // Move activity has source square, target square, // indices of items on map, and quantities of same. const tripoint destination = g->u.activity.coords[0]; const tripoint source = g->u.activity.placement; bool from_vehicle = g->u.activity.values[0]; bool to_vehicle = g->u.activity.values[1]; std::list<int> indices; std::list<int> quantities; // Note i = 4, skipping first few elements. for( size_t i = 2; i < g->u.activity.values.size(); i += 2 ) { indices.push_back( g->u.activity.values[i] ); quantities.push_back( g->u.activity.values[i + 1] ); } // Nuke the current activity, leaving the backlog alone. g->u.activity = player_activity(); // *puts on 3d glasses from 90s cereal box* move_items( source, from_vehicle, destination, to_vehicle, indices, quantities ); if( !indices.empty() ) { g->u.assign_activity( activity_id( "ACT_MOVE_ITEMS" ) ); g->u.activity.placement = source; g->u.activity.coords.push_back( destination ); g->u.activity.values.push_back( from_vehicle ); g->u.activity.values.push_back( to_vehicle ); while( !indices.empty() ) { g->u.activity.values.push_back( indices.front() ); indices.pop_front(); g->u.activity.values.push_back( quantities.front() ); quantities.pop_front(); } } }
void player_activity::do_turn( player &p ) { // Should happen before activity or it may fail du to 0 moves if( *this && type->will_refuel_fires() ) { try_refuel_fire( p ); } if( type->based_on() == based_on_type::TIME ) { moves_left -= 100; } else if( type->based_on() == based_on_type::SPEED ) { if( p.moves <= moves_left ) { moves_left -= p.moves; p.moves = 0; } else { p.moves -= moves_left; moves_left = 0; } } // This might finish the activity (set it to null) type->call_do_turn( this, &p ); if( *this && type->rooted() ) { p.rooted(); p.pause(); } if( *this && moves_left <= 0 ) { // Note: For some activities "finish" is a misnomer; that's why we explicitly check if the // type is ACT_NULL below. if( !( type->call_finish( this, &p ) ) ) { // "Finish" is never a misnomer for any activity without a finish function set_to_null(); } } if( !*this ) { // Make sure data of previous activity is cleared p.activity = player_activity(); if( !p.backlog.empty() && p.backlog.front().auto_resume ) { p.activity = p.backlog.front(); p.backlog.pop_front(); } // If whatever activity we were doing forced us to pick something up to // handle it, drop any overflow that may have caused p.drop_invalid_inventory(); } }
void game::place_construction(constructable *con) { refresh_all(); inventory total_inv; total_inv.form_from_map(this, point(u.posx, u.posy), PICKUP_RANGE); total_inv.add_stack(u.inv_dump()); std::vector<point> valid; for (int x = u.posx - 1; x <= u.posx + 1; x++) { for (int y = u.posy - 1; y <= u.posy + 1; y++) { if (x == u.posx && y == u.posy) y++; construct test; bool place_okay = (test.*(con->able))(this, point(x, y)); for (int i = 0; i < con->stages.size() && !place_okay; i++) { if (m.ter(x, y) == con->stages[i].terrain) place_okay = true; } if (place_okay) { // Make sure we're not trying to continue a construction that we can't finish int starting_stage = 0, max_stage = 0; for (int i = 0; i < con->stages.size(); i++) { if (m.ter(x, y) == con->stages[i].terrain) starting_stage = i + 1; if (player_can_build(u, total_inv, con, i, true)) max_stage = i; } if (max_stage >= starting_stage) { valid.push_back(point(x, y)); m.drawsq(w_terrain, u, x, y, true, false); wrefresh(w_terrain); } } } } mvprintz(0, 0, c_red, _("Pick a direction in which to construct:")); int dirx, diry; get_direction(this, dirx, diry, input()); if (dirx == -2) { add_msg(_("Invalid direction.")); return; } dirx += u.posx; diry += u.posy; bool point_is_okay = false; for (int i = 0; i < valid.size() && !point_is_okay; i++) { if (valid[i].x == dirx && valid[i].y == diry) point_is_okay = true; } if (!point_is_okay) { add_msg(_("You cannot build there!")); return; } // Figure out what stage to start at, and what stage is the maximum int starting_stage = 0, max_stage = 0; for (int i = 0; i < con->stages.size(); i++) { if (m.ter(dirx, diry) == con->stages[i].terrain) starting_stage = i + 1; if (player_can_build(u, total_inv, con, i, true)) max_stage = i; } u.activity = player_activity(ACT_BUILD, con->stages[starting_stage].time*1000, con->id); u.moves = 0; std::vector<int> stages; for (int i = starting_stage; i <= max_stage; i++) stages.push_back(i); u.activity.values = stages; u.activity.placement = point(dirx, diry); }
void player_activity::finish( player *p ) { switch( type ) { case ACT_RELOAD: activity_handlers::reload_finish( this, p ); break; case ACT_READ: p->do_read( &( p->i_at( position ) ) ); if( type == ACT_NULL ) { add_msg( _( "You finish reading." ) ); } break; case ACT_WAIT: case ACT_WAIT_WEATHER: add_msg( _( "You finish waiting." ) ); type = ACT_NULL; break; case ACT_WAIT_NPC: add_msg( _( "%s finishes with you..." ), str_values[0].c_str() ); type = ACT_NULL; break; case ACT_CRAFT: p->complete_craft(); type = ACT_NULL; break; case ACT_LONGCRAFT: { int batch_size = values.front(); p->complete_craft(); type = ACT_NULL; // Workaround for a bug where longcraft can be unset in complete_craft(). if( p->making_would_work( p->lastrecipe, batch_size ) ) { p->make_all_craft( p->lastrecipe, batch_size ); } } break; case ACT_FORAGE: activity_handlers::forage_finish( this, p ); type = ACT_NULL; break; case ACT_DISASSEMBLE: p->complete_disassemble(); break; case ACT_BUTCHER: activity_handlers::butcher_finish( this, p ); break; case ACT_LONGSALVAGE: activity_handlers::longsalvage_finish( this, p ); break; case ACT_VEHICLE: activity_handlers::vehicle_finish( this, p ); break; case ACT_BUILD: complete_construction(); type = ACT_NULL; break; case ACT_TRAIN: activity_handlers::train_finish( this, p ); break; case ACT_FIRSTAID: activity_handlers::firstaid_finish( this, p ); break; case ACT_FISH: activity_handlers::fish_finish( this, p ); break; case ACT_PICKAXE: activity_handlers::pickaxe_finish( this, p ); type = ACT_NULL; break; case ACT_BURROW: activity_handlers::burrow_finish( this, p ); type = ACT_NULL; break; case ACT_VIBE: add_msg( m_good, _( "You feel much better." ) ); type = ACT_NULL; break; case ACT_MAKE_ZLAVE: activity_handlers::make_zlave_finish( this, p ); type = ACT_NULL; break; case ACT_PICKUP: case ACT_MOVE_ITEMS: // Only do nothing if the item being picked up doesn't need to be equipped. // If it needs to be equipped, our activity_handler::pickup_finish() does so. // This is primarily used by AIM to advance moves while moving items around. activity_handlers::pickup_finish( this, p ); break; case ACT_START_FIRE: activity_handlers::start_fire_finish( this, p ); break; case ACT_OPEN_GATE: activity_handlers::open_gate_finish( this, p ); type = ACT_NULL; break; case ACT_HOTWIRE_CAR: activity_handlers::hotwire_finish( this, p ); break; case ACT_AIM: // Aim bails itself by resetting itself every turn, // you only re-enter if it gets set again. break; case ACT_ATM: // ATM sets index to 0 to indicate it's finished. if( !index ) { type = ACT_NULL; } break; case ACT_START_ENGINES: activity_handlers::start_engines_finish( this, p ); type = ACT_NULL; break; case ACT_OXYTORCH: activity_handlers::oxytorch_finish( this, p ); type = ACT_NULL; break; case ACT_CRACKING: activity_handlers::cracking_finish( this, p ); type = ACT_NULL; break; case ACT_REPAIR_ITEM: // Unsets activity (if needed) inside function activity_handlers::repair_item_finish( this, p ); break; case ACT_MEND_ITEM: activity_handlers::mend_item_finish( this, p ); type = ACT_NULL; break; case ACT_GUNMOD_ADD: activity_handlers::gunmod_add_finish( this, p ); type = ACT_NULL; break; default: type = ACT_NULL; } if( type == ACT_NULL ) { // Make sure data of previous activity is cleared p->activity = player_activity(); if( !p->backlog.empty() && p->backlog.front().auto_resume ) { p->activity = p->backlog.front(); p->backlog.pop_front(); } } }
void game::make_craft(recipe *making) { u.activity = player_activity(ACT_CRAFT, making->time, making->id); u.moves = 0; }
void activity_on_turn_move_loot( player_activity &, player &p ) { const activity_id act_move_loot = activity_id( "ACT_MOVE_LOOT" ); auto &mgr = zone_manager::get_manager(); if( g->m.check_vehicle_zones( g->get_levz() ) ) { mgr.cache_vzones(); } const auto abspos = g->m.getabs( p.pos() ); const auto &src_set = mgr.get_near( zone_type_id( "LOOT_UNSORTED" ), abspos ); vehicle *src_veh, *dest_veh; int src_part, dest_part; // Nuke the current activity, leaving the backlog alone. p.activity = player_activity(); // sort source tiles by distance const auto &src_sorted = get_sorted_tiles_by_distance( abspos, src_set ); if( !mgr.is_sorting() ) { mgr.start_sort( src_sorted ); } for( auto &src : src_sorted ) { const auto &src_loc = g->m.getlocal( src ); if( !g->m.inbounds( src_loc ) ) { if( !g->m.inbounds( p.pos() ) ) { // p is implicitly an NPC that has been moved off the map, so reset the activity // and unload them p.assign_activity( act_move_loot ); p.set_moves( 0 ); g->reload_npcs(); mgr.end_sort(); return; } std::vector<tripoint> route; route = g->m.route( p.pos(), src_loc, p.get_pathfinding_settings(), p.get_path_avoid() ); if( route.empty() ) { // can't get there, can't do anything, skip it continue; } p.set_destination( route, player_activity( act_move_loot ) ); mgr.end_sort(); return; } bool is_adjacent_or_closer = square_dist( p.pos(), src_loc ) <= 1; // skip tiles in IGNORE zone and tiles on fire // (to prevent taking out wood off the lit brazier) // and inaccessible furniture, like filled charcoal kiln if( mgr.has( zone_type_id( "LOOT_IGNORE" ), src ) || g->m.get_field( src_loc, fd_fire ) != nullptr || !g->m.can_put_items_ter_furn( src_loc ) ) { continue; } // the boolean in this pair being true indicates the item is from a vehicle storage space auto items = std::vector<std::pair<item *, bool>>(); //Check source for cargo part //map_stack and vehicle_stack are different types but inherit from item_stack // TODO: use one for loop if( const cata::optional<vpart_reference> vp = g->m.veh_at( src_loc ).part_with_feature( "CARGO", false ) ) { src_veh = &vp->vehicle(); src_part = vp->part_index(); for( auto &it : src_veh->get_items( src_part ) ) { items.push_back( std::make_pair( &it, true ) ); } } else { src_veh = nullptr; src_part = -1; } for( auto &it : g->m.i_at( src_loc ) ) { items.push_back( std::make_pair( &it, false ) ); } //Skip items that have already been processed for( auto it = items.begin() + mgr.get_num_processed( src ); it < items.end(); it++ ) { mgr.increment_num_processed( src ); const auto thisitem = it->first; if( thisitem->made_of_from_type( LIQUID ) ) { // skip unpickable liquid continue; } // Only if it's from a vehicle do we use the vehicle source location information. vehicle *this_veh = it->second ? src_veh : nullptr; const int this_part = it->second ? src_part : -1; const auto id = mgr.get_near_zone_type_for_item( *thisitem, abspos ); // checks whether the item is already on correct loot zone or not // if it is, we can skip such item, if not we move the item to correct pile // think empty bag on food pile, after you ate the content if( !mgr.has( id, src ) ) { const auto &dest_set = mgr.get_near( id, abspos ); for( auto &dest : dest_set ) { const auto &dest_loc = g->m.getlocal( dest ); //Check destination for cargo part if( const cata::optional<vpart_reference> vp = g->m.veh_at( dest_loc ).part_with_feature( "CARGO", false ) ) { dest_veh = &vp->vehicle(); dest_part = vp->part_index(); } else { dest_veh = nullptr; dest_part = -1; } // skip tiles with inaccessible furniture, like filled charcoal kiln if( !g->m.can_put_items_ter_furn( dest_loc ) ) { continue; } units::volume free_space; // if there's a vehicle with space do not check the tile beneath if( dest_veh ) { free_space = dest_veh->free_volume( dest_part ); } else { free_space = g->m.free_volume( dest_loc ); } // check free space at destination if( free_space >= thisitem->volume() ) { // before we move any item, check if player is at or // adjacent to the loot source tile if( !is_adjacent_or_closer ) { std::vector<tripoint> route; bool adjacent = false; // get either direct route or route to nearest adjacent tile if // source tile is impassable if( g->m.passable( src_loc ) ) { route = g->m.route( p.pos(), src_loc, p.get_pathfinding_settings(), p.get_path_avoid() ); } else { // immpassable source tile (locker etc.), // get route to nerest adjacent tile instead route = route_adjacent( p, src_loc ); adjacent = true; } // check if we found path to source / adjacent tile if( route.empty() ) { add_msg( m_info, _( "%s can't reach the source tile. Try to sort out loot without a cart." ), p.disp_name() ); mgr.end_sort(); return; } // shorten the route to adjacent tile, if necessary if( !adjacent ) { route.pop_back(); } // set the destination and restart activity after player arrives there // we don't need to check for safe mode, // activity will be restarted only if // player arrives on destination tile p.set_destination( route, player_activity( act_move_loot ) ); mgr.end_sort(); return; } move_item( p, *thisitem, thisitem->count(), src_loc, dest_loc, this_veh, this_part ); // moved item away from source so decrement mgr.decrement_num_processed( src ); break; } } if( p.moves <= 0 ) { // Restart activity and break from cycle. p.assign_activity( act_move_loot ); mgr.end_sort(); return; } } } } // If we got here without restarting the activity, it means we're done add_msg( m_info, string_format( _( "%s sorted out every item possible." ), p.disp_name() ) ); mgr.end_sort(); }
void player_activity::finish( player *p ) { switch (type) { case ACT_RELOAD: activity_handlers::reload_finish( this, p ); break; case ACT_READ: p->do_read(&(p->i_at(position))); if (type == ACT_NULL) { add_msg(_("You finish reading.")); } break; case ACT_WAIT: case ACT_WAIT_WEATHER: add_msg(_("You finish waiting.")); type = ACT_NULL; break; case ACT_CRAFT: p->complete_craft(); type = ACT_NULL; break; case ACT_LONGCRAFT: { int batch_size = values.front(); p->complete_craft(); type = ACT_NULL; // Workaround for a bug where longcraft can be unset in complete_craft(). if( p->making_would_work( p->lastrecipe, batch_size ) ) { p->make_all_craft( p->lastrecipe, batch_size ); } } break; case ACT_FORAGE: activity_handlers::forage_finish( this, p ); type = ACT_NULL; break; case ACT_DISASSEMBLE: p->complete_disassemble(); type = ACT_NULL; break; case ACT_BUTCHER: activity_handlers::butcher_finish( this, p ); type = ACT_NULL; break; case ACT_LONGSALVAGE: activity_handlers::longsalvage_finish( this, p ); break; case ACT_VEHICLE: activity_handlers::vehicle_finish( this, p ); break; case ACT_BUILD: complete_construction(); type = ACT_NULL; break; case ACT_TRAIN: activity_handlers::train_finish( this, p ); break; case ACT_FIRSTAID: activity_handlers::firstaid_finish( this, p ); break; case ACT_FISH: activity_handlers::fish_finish( this, p ); break; case ACT_PICKAXE: activity_handlers::pickaxe_finish( this, p ); type = ACT_NULL; break; case ACT_BURROW: activity_handlers::burrow_finish( this, p ); type = ACT_NULL; break; case ACT_VIBE: add_msg(m_good, _("You feel much better.")); type = ACT_NULL; break; case ACT_MAKE_ZLAVE: activity_handlers::make_zlave_finish( this, p ); type = ACT_NULL; break; case ACT_PICKUP: case ACT_MOVE_ITEMS: // Do nothing, the only way this happens is if we set this activity after // entering the advanced inventory menu as an activity, and we want it to play out. break; case ACT_START_FIRE: activity_handlers::start_fire_finish( this, p ); break; case ACT_HOTWIRE_CAR: activity_handlers::hotwire_finish( this, p ); break; case ACT_AIM: // Aim bails itself by resetting itself every turn, // you only re-enter if it gets set again. break; case ACT_ATM: // ATM sets index to 0 to indicate it's finished. if (!index) { type = ACT_NULL; } break; case ACT_START_ENGINES: activity_handlers::start_engines_finish( this, p ); type = ACT_NULL; break; case ACT_OXYTORCH: activity_handlers::oxytorch_finish( this, p ); type = ACT_NULL; break; default: type = ACT_NULL; } if (type == ACT_NULL) { // Make sure data of previous activity is cleared p->activity = player_activity(); if( !p->backlog.empty() && p->backlog.front().auto_resume ) { p->activity = p->backlog.front(); p->backlog.pop_front(); } } }