Skip to content

Commit

Permalink
Added multiple camp construction workers (#55816)
Browse files Browse the repository at this point in the history
  • Loading branch information
PatrikLundell authored Mar 9, 2022
1 parent 97843a3 commit 7354e9d
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 62 deletions.
12 changes: 10 additions & 2 deletions src/basecamp.h
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,14 @@ class basecamp
bool must_feed, const std::string &desc, bool group,
const std::vector<item *> &equipment,
const std::map<skill_id, int> &required_skills = {} );
comp_list start_multi_mission( const mission_id &miss_id,
bool must_feed, const std::string &desc,
// const std::vector<item*>& equipment, // No support for extracting equipment from recipes currently..
const skill_id &skill_tested, int skill_level );
comp_list start_multi_mission( const mission_id &miss_id,
bool must_feed, const std::string &desc,
// const std::vector<item*>& equipment, // No support for extracting equipment from recipes currently..
const std::map<skill_id, int> &required_skills = {} );
void start_upgrade( const mission_id &miss_id );
std::string om_upgrade_description( const std::string &bldg, bool trunc = false ) const;
void start_menial_labor();
Expand Down Expand Up @@ -350,9 +358,9 @@ class basecamp
bool farm_return( const mission_id &miss_id, const tripoint_abs_omt &omt_tgt );
void fortifications_return( const mission_id miss_id );
bool salt_water_pipe_swamp_return( const mission_id &miss_id,
const time_duration work_days );
const comp_list &npc_list );
bool salt_water_pipe_return( const mission_id &miss_id,
const time_duration work_days );
const comp_list &npc_list );

void combat_mission_return( const mission_id &miss_id );
void validate_assignees();
Expand Down
182 changes: 133 additions & 49 deletions src/faction_camp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1690,6 +1690,70 @@ npc_ptr basecamp::start_mission( const mission_id &miss_id, time_duration durati
return comp;
}

comp_list basecamp::start_multi_mission( const mission_id &miss_id,
bool must_feed, const std::string &desc,
// const std::vector<item*>& equipment, // No support for extracting equipment from recipes currently..
const skill_id &skill_tested, int skill_level )
{
std::map<skill_id, int> required_skills;
required_skills[skill_tested] = skill_level;
return start_multi_mission( miss_id, must_feed, desc, required_skills );
}

comp_list basecamp::start_multi_mission( const mission_id &miss_id,
bool must_feed, const std::string &desc,
// const std::vector<item*>& equipment, // No support for extracting equipment from recipes currently..
const std::map<skill_id, int> &required_skills )
{

const recipe &making = recipe_id( miss_id.parameters ).obj();
comp_list result;

time_duration work_days;

while( true ) { // We'll break out of the loop when all the workers have been assigned
work_days = base_camps::to_workdays( making.batch_duration(
get_player_character() ) / ( result.size() + 1 ) );

if( must_feed && camp_food_supply() < time_to_food( work_days * ( result.size() + 1 ) ) ) {
if( result.empty() ) {
popup( _( "You don't have enough food stored to feed your companion for this task." ) );
return result;
} else {
popup( _( "You don't have enough food stored to feed a larger work crew." ) );
break;
}
}

// We should allocate the full set of tools to each companion sent off on a mission,
// but currently recipes only provides a function to check for the full set of requirement,
// with no access to the tool subset, so no tools are actually assigned.
npc_ptr comp = talk_function::individual_mission( omt_pos, base_camps::id, desc, miss_id,
false, {}, required_skills, !result.empty() );

if( comp == nullptr ) {
break;
} else {
result.push_back( comp );
}
}

if( result.empty() ) {
return result;
} else {
work_days = base_camps::to_workdays( making.batch_duration(
get_player_character() ) / result.size() );

for( npc_ptr &comp : result ) {
comp->companion_mission_time_ret = calendar::turn + work_days;
}
if( must_feed ) {
camp_food_supply( work_days * result.size() );
}
return result;
}
}

void basecamp::start_upgrade( const mission_id &miss_id )
{
const recipe &making = recipe_id( miss_id.parameters ).obj();
Expand All @@ -1711,23 +1775,21 @@ void basecamp::start_upgrade( const mission_id &miss_id )
return;
}

time_duration work_days = base_camps::to_workdays( making.batch_duration(
get_player_character() ) );
npc_ptr comp = nullptr;
comp_list comp;
if( making.required_skills.empty() ) {
if( making.skill_used.is_valid() ) {
comp = start_mission( miss_id, work_days, must_feed,
_( "begins to upgrade the camp…" ), false, {},
making.skill_used, making.difficulty );
comp = start_multi_mission( miss_id, must_feed,
_( "begins to upgrade the camp…" ),
making.skill_used, making.difficulty );
} else {
comp = start_mission( miss_id, work_days, must_feed,
_( "begins to upgrade the camp…" ), false, {} );
comp = start_multi_mission( miss_id, must_feed,
_( "begins to upgrade the camp…" ) );
}
} else {
comp = start_mission( miss_id, work_days, must_feed, _( "begins to upgrade the camp…" ),
false, {}, making.required_skills );
comp = start_multi_mission( miss_id, must_feed, _( "begins to upgrade the camp…" ),
making.required_skills );
}
if( comp == nullptr ) {
if( comp.empty() ) {
return;
}
components.consume_components();
Expand Down Expand Up @@ -2488,9 +2550,6 @@ bool basecamp::common_salt_water_pipe_construction(
{
const recipe &making = recipe_id(
miss_id.parameters ).obj(); // Actually a template recipe that we'll rotate and mirror as required.
time_duration work_days = base_camps::to_workdays( making.batch_duration(
get_player_character() ) );

int remaining_segments = pipe->segments.size() - 1;

if( segment_number == 0 ) {
Expand All @@ -2504,25 +2563,25 @@ bool basecamp::common_salt_water_pipe_construction(
return false;
}

npc_ptr comp;
comp_list comp;

if( segment_number == 0 ) {
comp = start_mission( miss_id, work_days, true,
_( "Start constructing salt water pipes…" ), false, {},
making.required_skills );
comp = start_multi_mission( miss_id, true,
_( "Start constructing salt water pipes…" ),
making.required_skills );
} else {
comp = start_mission( miss_id, work_days, true,
_( "Continue constructing salt water pipes…" ), false, {},
making.required_skills );
comp = start_multi_mission( miss_id, true,
_( "Continue constructing salt water pipes…" ),
making.required_skills );
}

if( comp != nullptr ) {
if( !comp.empty() ) {
components.consume_components();
update_in_progress( miss_id.parameters, miss_id.dir.value() ); // Dir will always have a value
pipe->segments[segment_number].started = true;
}

return comp != nullptr;
return !comp.empty();
}

void basecamp::start_salt_water_pipe( const mission_id &miss_id )
Expand Down Expand Up @@ -3122,6 +3181,29 @@ npc_ptr basecamp::emergency_recall( const mission_id miss_id )
npc_ptr comp = talk_function::companion_choose_return( omt_pos, base_camps::id, miss_id,
calendar::turn - 24_hours, false );
if( comp != nullptr ) {

// Special handing for camp upgrades. If multiple companions are assigned, the remaining time
// is divided between the remaining workers. Note that this logic relies on there being only
// a single instance of each construction active, and thus all workers assigned to that
// blueprint are on the same mission. Won't work with e.g. crafting, as different instances of
// the same crafting mission may be in various stages of completion concurrently.
if( comp->get_companion_mission().miss_id.id == Camp_Upgrade ) {
comp_list npc_list = get_mission_workers( comp->get_companion_mission().miss_id );

if( npc_list.size() > 1 ) {
time_duration remaining_time = comp->companion_mission_time_ret - calendar::turn;
if( remaining_time > time_duration::from_turns( 0 ) ) {
remaining_time = remaining_time * npc_list.size() / ( npc_list.size() - 1 );
}

for( npc_ptr &worker : npc_list ) {
if( worker != comp ) {
worker->companion_mission_time_ret = calendar::turn + remaining_time;
}
}
}
}

const std::string return_msg = _( "responds to the emergency recall…" );
finish_return( *comp, false, return_msg, skill_menial.str(), 0, true );
}
Expand All @@ -3144,14 +3226,19 @@ bool basecamp::upgrade_return( const mission_id &miss_id )
const tripoint_abs_omt upos = e->second.pos;
const recipe &making = recipe_id( bldg ).obj();

time_duration work_days = base_camps::to_workdays( making.batch_duration(
get_player_character() ) );
npc_ptr comp = companion_choose_return( miss_id, work_days );

if( comp == nullptr ) {
comp_list npc_list = get_mission_workers( miss_id );
if( npc_list.empty() ) {
return false;
}

std::string companion_list;
for( const npc_ptr &comp : npc_list ) {
if( comp != npc_list[0] ) {
companion_list += ", ";
}
companion_list += comp->disp_name();
}

bool mirror_horizontal;
bool mirror_vertical;
int rotation;
Expand All @@ -3162,20 +3249,20 @@ bool basecamp::upgrade_return( const mission_id &miss_id )
mirror_vertical,
rotation,
"%s failed to build the %s upgrade",
comp->disp_name() ) ) {
companion_list ) ) {
return false;
}

if( making.get_blueprint().str() == faction_expansion_salt_water_pipe_swamp_N ) {
return salt_water_pipe_swamp_return( miss_id, work_days );
return salt_water_pipe_swamp_return( miss_id, npc_list );
} else if( making.get_blueprint().str() == faction_expansion_salt_water_pipe_N ) {
return salt_water_pipe_return( miss_id, work_days );
return salt_water_pipe_return( miss_id, npc_list );
}

if( !run_mapgen_update_func( making.get_blueprint(), upos, nullptr, true, mirror_horizontal,
mirror_vertical, rotation ) ) {
popup( _( "%s failed to build the %s upgrade, perhaps there is a vehicle in the way." ),
comp->disp_name(),
companion_list,
making.get_blueprint().str() );
return false;
}
Expand All @@ -3184,7 +3271,10 @@ bool basecamp::upgrade_return( const mission_id &miss_id )

const std::string msg = _( "returns from upgrading the camp having earned a bit of "
"experience…" );
finish_return( *comp, false, msg, "construction", making.difficulty );

for( const npc_ptr &comp : npc_list ) {
finish_return( *comp, false, msg, "construction", making.difficulty );
}

return true;
}
Expand Down Expand Up @@ -3378,14 +3468,9 @@ void salt_water_pipe_orientation_adjustment( const point dir, bool &orthogonal,
}

bool basecamp::salt_water_pipe_swamp_return( const mission_id &miss_id,
const time_duration work_days )
const comp_list &npc_list )
{
const point dir = miss_id.dir.value(); // Will always have a value
npc_ptr comp = companion_choose_return( miss_id, work_days );

if( comp == nullptr ) {
return false;
}

expansion_salt_water_pipe *pipe;

Expand Down Expand Up @@ -3459,22 +3544,19 @@ bool basecamp::salt_water_pipe_swamp_return( const mission_id &miss_id,
update_provides( miss_id.parameters, e->second );
update_resources( miss_id.parameters );

finish_return( *comp, true,
_( "returns from construction of the salt water pipe swamp segment…" ), "construction", 2 );
for( const npc_ptr &comp : npc_list ) {
finish_return( *comp, true,
_( "returns from construction of the salt water pipe swamp segment…" ), "construction", 2 );
}

return true;
}

bool basecamp::salt_water_pipe_return( const mission_id &miss_id,
const time_duration work_days )
const comp_list &npc_list )
{
const recipe &making = recipe_id( miss_id.parameters ).obj();
const point dir = miss_id.dir.value(); // Will always have a value
npc_ptr comp = companion_choose_return( miss_id, work_days );

if( comp == nullptr ) {
return false;
}

expansion_salt_water_pipe *pipe;

Expand Down Expand Up @@ -3569,8 +3651,10 @@ bool basecamp::salt_water_pipe_return( const mission_id &miss_id,
update_provides( miss_id.parameters, e->second );
update_resources( miss_id.parameters );

finish_return( *comp, true, _( "returns from construction of a salt water pipe segment…" ),
"construction", 2 );
for( const npc_ptr &comp : npc_list ) {
finish_return( *comp, true, _( "returns from construction of a salt water pipe segment…" ),
"construction", 2 );
}

return true;
}
Expand Down
28 changes: 20 additions & 8 deletions src/mission_companion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1204,18 +1204,18 @@ bool talk_function::handle_outpost_mission( const mission_entry &cur_key, npc &p

npc_ptr talk_function::individual_mission( npc &p, const std::string &desc,
const mission_id &miss_id, bool group, const std::vector<item *> &equipment,
const std::map<skill_id, int> &required_skills )
const std::map<skill_id, int> &required_skills, bool silent_failure )
{
const tripoint_abs_omt omt_pos = p.global_omt_location();
return individual_mission( omt_pos, p.companion_mission_role_id, desc, miss_id, group,
equipment, required_skills );
equipment, required_skills, silent_failure );
}
npc_ptr talk_function::individual_mission( const tripoint_abs_omt &omt_pos,
const std::string &role_id, const std::string &desc,
const mission_id &miss_id, bool group, const std::vector<item *> &equipment,
const std::map<skill_id, int> &required_skills )
const std::map<skill_id, int> &required_skills, bool silent_failure )
{
npc_ptr comp = companion_choose( required_skills );
npc_ptr comp = companion_choose( required_skills, silent_failure );
if( comp == nullptr ) {
return comp;
}
Expand Down Expand Up @@ -2412,7 +2412,8 @@ std::vector<comp_rank> talk_function::companion_rank( const std::vector<npc_ptr>
return adjusted;
}

npc_ptr talk_function::companion_choose( const std::map<skill_id, int> &required_skills )
npc_ptr talk_function::companion_choose( const std::map<skill_id, int> &required_skills,
bool silent_failure )
{
Character &player_character = get_player_character();
std::vector<npc_ptr> available;
Expand Down Expand Up @@ -2457,15 +2458,24 @@ npc_ptr talk_function::companion_choose( const std::map<skill_id, int> &required
}
}
if( available.empty() ) {
popup( _( "You don't have any companions to send out…" ) );
if( !silent_failure ) {
popup( _( "You don't have any companions to send out…" ) );
}
return nullptr;
}
std::vector<uilist_entry> npc_menu;
available = companion_sort( available, required_skills );
std::vector<comp_rank> rankings = companion_rank( available );

int x = 0;
std::string menu_header = left_justify( _( "Who do you want to send?" ), 51 );
std::string menu_header;

if( silent_failure ) {
menu_header = left_justify( _( "Do you want to send someone else?" ), 51 );
} else {
menu_header = left_justify( _( "Who do you want to send?" ), 51 );
}

if( required_skills.empty() ) {
menu_header += _( "[ COMBAT : SURVIVAL : INDUSTRY ]" );
}
Expand Down Expand Up @@ -2514,7 +2524,9 @@ npc_ptr talk_function::companion_choose( const std::map<skill_id, int> &required
}
const size_t npc_choice = uilist( menu_header, npc_menu );
if( npc_choice >= available.size() ) {
popup( _( "You choose to send no one…" ), npc_choice );
if( !silent_failure ) {
popup( _( "You choose to send no one…" ), npc_choice );
}
return nullptr;
}

Expand Down
Loading

0 comments on commit 7354e9d

Please sign in to comment.