Skip to content

Commit

Permalink
NPC jobs at camps (#34289)
Browse files Browse the repository at this point in the history
* assign NPCs to camp and give them jobs

* remove jobs that arent implemented yet

* change access map to avoid inserting new key

* remove crafting from job map - not merged yet

* change descriptions of jobs

* Update src/faction_camp.cpp

Co-Authored-By: Jianxiang Wang (王健翔) <[email protected]>

* astyle
  • Loading branch information
davidpwbrown authored and ZhilkinSerg committed Oct 11, 2019
1 parent 7014112 commit 126c27b
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 35 deletions.
6 changes: 6 additions & 0 deletions data/json/npcs/TALK_COMMON_ALLY.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@
"topic": "TALK_FRIEND_GUARD",
"effect": "assign_guard"
},
{
"text": "I want to assign you to work at this camp.",
"condition": { "npc_at_om_location": "FACTION_CAMP_ANY" },
"topic": "TALK_FRIEND_GUARD",
"effect": "assign_camp"
},
{ "text": "Let's talk about your current activity.", "topic": "TALK_ACTIVITIES" },
{ "text": "Let's talk about faction camps.", "topic": "TALK_CAMP_GENERAL" },
{
Expand Down
18 changes: 13 additions & 5 deletions src/basecamp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ void basecamp::validate_assignees()
{
for( auto it2 = assigned_npcs.begin(); it2 != assigned_npcs.end(); ) {
auto ptr = *it2;
if( ptr->mission != NPC_MISSION_GUARD_ALLY || ptr->global_omt_location() != omt_pos ||
if( ptr->mission != NPC_MISSION_ASSIGNED_CAMP || ptr->global_omt_location() != omt_pos ||
ptr->has_companion_mission() ) {
it2 = assigned_npcs.erase( it2 );
} else {
Expand All @@ -484,12 +484,20 @@ void basecamp::validate_assignees()
if( !npc_to_add ) {
continue;
}
if( npc_to_add->global_omt_location() == omt_pos &&
npc_to_add->mission == NPC_MISSION_GUARD_ALLY &&
!npc_to_add->has_companion_mission() ) {
assigned_npcs.push_back( npc_to_add );
if( std::find( assigned_npcs.begin(), assigned_npcs.end(), npc_to_add ) != assigned_npcs.end() ) {
continue;
} else {
if( npc_to_add->global_omt_location() == omt_pos &&
npc_to_add->mission == NPC_MISSION_ASSIGNED_CAMP &&
!npc_to_add->has_companion_mission() ) {
assigned_npcs.push_back( npc_to_add );
}
}
}
// remove duplicates - for legacy handling.
std::sort( assigned_npcs.begin(), assigned_npcs.end() );
auto last = std::unique( assigned_npcs.begin(), assigned_npcs.end() );
assigned_npcs.erase( last, assigned_npcs.end() );
}

std::vector<npc_ptr> basecamp::get_npcs_assigned()
Expand Down
1 change: 1 addition & 0 deletions src/basecamp.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ class basecamp
bool by_radio );
std::string om_upgrade_description( const std::string &bldg, bool trunc = false ) const;
void start_menial_labor();
void job_assignment_ui();
void start_crafting( const std::string &cur_id, const point &cur_dir,
const std::string &type, const std::string &miss_id,
bool by_radio = false );
Expand Down
5 changes: 5 additions & 0 deletions src/faction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,11 @@ int npc::faction_display( const catacurses::window &fac_w, const int width ) con
current_status += _( "Guarding" );
}
mvwprintz( fac_w, point( width, ++y ), status_col, current_status );
if( is_stationed ) {
std::string current_job = _( "Basecamp job : " );
current_job += npc_job_name( job );
mvwprintz( fac_w, point( width, ++y ), col, current_job );
}

const std::pair <std::string, nc_color> condition = hp_description();
mvwprintz( fac_w, point( width, ++y ), condition.second, _( "Condition : " ) + condition.first );
Expand Down
112 changes: 111 additions & 1 deletion src/faction_camp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "editmap.h"
#include "game.h"
#include "iexamine.h"
#include "input.h"
#include "item_group.h"
#include "itype.h"
#include "line.h"
Expand Down Expand Up @@ -749,6 +750,15 @@ void basecamp::get_available_missions( mission_data &mission_key, bool by_radio
"sorted using the normal rules for automatic zone "
"sorting." ) );
mission_key.add( "Reset Sort Points", _( "Reset Sort Points" ), entry );
validate_assignees();
std::vector<npc_ptr> npc_list = get_npcs_assigned();
entry = string_format( _( "Notes:\n"
"Assign repeating job duties to NPCs stationed here.\n"
"Difficulty: N/A \n"
"Effects:\n"
"\n\nRisk: None\n"
"Time: Ongoing" ) );
mission_key.add( "Assign Jobs", _( "Assign Jobs" ), entry );
}
}

Expand All @@ -775,7 +785,6 @@ void basecamp::get_available_missions( mission_data &mission_key, bool by_radio
cata::nullopt, entry, avail );
}
}

if( has_provides( "sorting" ) ) {
if( !by_radio ) {
comp_list npc_list = get_mission_workers( "_faction_camp_menial" );
Expand Down Expand Up @@ -1335,6 +1344,10 @@ bool basecamp::handle_mission( const std::string &miss_id, const cata::optional<
menial_return();
}

if( miss_id == "Assign Jobs" ) {
job_assignment_ui();
}

if( miss_id == "Cut Logs" ) {
start_cut_logs();
} else if( miss_id == "Recover Log Cutter" ) {
Expand Down Expand Up @@ -1558,6 +1571,103 @@ void basecamp::start_upgrade( const std::string &bldg, const point &dir,
}
}

void basecamp::job_assignment_ui()
{
int term_x = TERMY > FULL_SCREEN_HEIGHT ? ( TERMY - FULL_SCREEN_HEIGHT ) / 2 : 0;
int term_y = TERMX > FULL_SCREEN_WIDTH ? ( TERMX - FULL_SCREEN_WIDTH ) / 2 : 0;

catacurses::window w_jobs = catacurses::newwin( FULL_SCREEN_HEIGHT, FULL_SCREEN_WIDTH,
point( term_y, term_x ) );
const int entries_per_page = FULL_SCREEN_HEIGHT - 4;
size_t selection = 0;
input_context ctxt( "FACTION MANAGER" );
ctxt.register_cardinal();
ctxt.register_updown();
ctxt.register_action( "ANY_INPUT" );
ctxt.register_action( "CONFIRM" );
ctxt.register_action( "QUIT" );
while( true ) {
werase( w_jobs );
// create a list of npcs stationed at this camp
std::vector<npc *> stationed_npcs;
for( const auto elem : get_npcs_assigned() ) {
if( elem ) {
stationed_npcs.push_back( elem.get() );
}
}
npc *cur_npc = nullptr;
// entries_per_page * page number
const size_t top_of_page = entries_per_page * ( selection / entries_per_page );
if( !stationed_npcs.empty() ) {
cur_npc = stationed_npcs[selection];
}

for( int i = 0; i < FULL_SCREEN_HEIGHT - 1; i++ ) {
mvwputch( w_jobs, point( 45, i ), BORDER_COLOR, LINE_XOXO );
}
draw_border( w_jobs );
const nc_color col = c_white;
const std::string no_npcs = _( "There are no npcs stationed here" );
if( !stationed_npcs.empty() ) {
draw_scrollbar( w_jobs, selection, entries_per_page, stationed_npcs.size(),
point( 0, 3 ) );
for( size_t i = top_of_page; i < stationed_npcs.size(); i++ ) {
const int y = i - top_of_page + 3;
trim_and_print( w_jobs, point( 1, y ), 43, selection == i ? hilite( col ) : col,
stationed_npcs[i]->disp_name() );
}
if( selection < stationed_npcs.size() ) {
std::string job_description;
if( cur_npc && cur_npc->has_job() ) {
// get the current NPCs job
job_description = npc_job_name( cur_npc->get_job() );
} else {
job_description = _( "No particular job" );
}
mvwprintz( w_jobs, point( 46, 3 ), c_light_gray, job_description );
} else {
mvwprintz( w_jobs, point( 46, 4 ), c_light_red, no_npcs );
}
} else {
mvwprintz( w_jobs, point( 46, 4 ), c_light_red, no_npcs );
}
mvwprintz( w_jobs, point( 1, FULL_SCREEN_HEIGHT - 1 ), c_light_gray,
_( "Press %s to change this workers job." ), ctxt.get_desc( "CONFIRM" ) );
wrefresh( w_jobs );
const std::string action = ctxt.handle_input();
if( action == "DOWN" ) {
selection++;
if( selection >= stationed_npcs.size() ) {
selection = 0;
}
} else if( action == "UP" ) {
if( selection == 0 ) {
selection = stationed_npcs.empty() ? 0 : stationed_npcs.size() - 1;
} else {
selection--;
}
} else if( action == "CONFIRM" ) {
if( !stationed_npcs.empty() ) {
uilist smenu;
smenu.text = _( "Assign which job?" );
int count = 0;
for( const auto &entry : all_jobs() ) {
smenu.addentry( count++, true, MENU_AUTOASSIGN, entry );
}

smenu.query();
if( smenu.ret >= 0 ) {
cur_npc->set_job( static_cast<npc_job>( smenu.ret ) );
}
}
} else if( action == "QUIT" ) {
break;
}
}

g->refresh_all();
}

void basecamp::start_menial_labor()
{
if( camp_food_supply() < time_to_food( 3_hours ) ) {
Expand Down
17 changes: 13 additions & 4 deletions src/npc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1967,7 +1967,7 @@ bool npc::is_assigned_to_camp() const
if( !bcp ) {
return false;
}
return !has_companion_mission() && mission == NPC_MISSION_GUARD_ALLY;
return !has_companion_mission() && mission == NPC_MISSION_ASSIGNED_CAMP;
}

bool npc::is_enemy() const
Expand Down Expand Up @@ -2515,13 +2515,22 @@ std::string npc_job_id( npc_job job )
return iter->second;
}

std::vector<std::string> all_jobs()
{
std::vector<std::string> ret;
for( int i = 0; i < NPCJOB_END; i++ ) {
ret.push_back( npc_job_name( static_cast<npc_job>( i ) ) );
}
return ret;
}

std::string npc_job_name( npc_job job )
{
switch( job ) {
case NPCJOB_NULL:
return _( "Not much" );
return _( "No particular job" );
case NPCJOB_COOKING:
return _( "Cooking" );
return _( "Cooking and butchering" );
case NPCJOB_MENIAL:
return _( "Tidying and cleaning" );
case NPCJOB_VEHICLES:
Expand All @@ -2539,7 +2548,7 @@ std::string npc_job_name( npc_job job )
case NPCJOB_HUSBANDRY:
return _( "Caring for the livestock" );
case NPCJOB_HUNTING:
return _( "Hunting for meat" );
return _( "Hunting and fishing" );
case NPCJOB_FORAGING:
return _( "Gathering edibles" );
default:
Expand Down
16 changes: 7 additions & 9 deletions src/npc.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,24 +128,21 @@ enum npc_job : int {
NPCJOB_END
};

static const std::map<npc_job, std::vector<activity_id>> job_duties = {
std::vector<std::string> all_jobs();
std::string npc_job_id( npc_job job );
std::string npc_job_name( npc_job job );

static std::map<npc_job, std::vector<activity_id>> job_duties = {
{ NPCJOB_NULL, std::vector<activity_id>{ activity_id( activity_id::NULL_ID() ) } },
{ NPCJOB_COOKING, std::vector<activity_id>{ activity_id( "ACT_MULTIPLE_CRAFT" ), activity_id( "ACT_MULTIPLE_BUTCHER" ) } },
{ NPCJOB_COOKING, std::vector<activity_id>{ activity_id( "ACT_MULTIPLE_BUTCHER" ) } },
{ NPCJOB_MENIAL, std::vector<activity_id>{ activity_id( "ACT_MOVE_LOOT" ), activity_id( "ACT_TIDY_UP" ) } },
{ NPCJOB_VEHICLES, std::vector<activity_id>{ activity_id( "ACT_VEHICLE_REPAIR" ), activity_id( "ACT_VEHICLE_DECONSTRUCTION" ) } },
{ NPCJOB_CONSTRUCTING, std::vector<activity_id>{ activity_id( "ACT_MULTIPLE_CONSTRUCTION" ) } },
{ NPCJOB_CRAFTING, std::vector<activity_id>{ activity_id( "ACT_MULTIPLE_CRAFT" ) } },
{ NPCJOB_SECURITY, std::vector<activity_id>{ activity_id( activity_id::NULL_ID() ) } }, // no corresponding activities yet.
{ NPCJOB_FARMING, std::vector<activity_id>{ activity_id( "ACT_MULTIPLE_FARM" ) } },
{ NPCJOB_LUMBERJACK, std::vector<activity_id>{ activity_id( "ACT_MULTIPLE_CHOP_TREES" ), activity_id( "ACT_MULTIPLE_CHOP_PLANKS" ) } },
{ NPCJOB_HUSBANDRY, std::vector<activity_id>{ activity_id( activity_id::NULL_ID() ) } }, // no corresponding activities yet.
{ NPCJOB_HUNTING, std::vector<activity_id>{ activity_id( "ACT_MULTIPLE_FISH" ) } },
{ NPCJOB_FORAGING, std::vector<activity_id>{ activity_id( activity_id::NULL_ID() ) } }, // no corresponding activities yet.
};

std::string npc_job_id( npc_job job );
std::string npc_job_name( npc_job job );

enum npc_mission : int {
NPC_MISSION_NULL = 0, // Nothing in particular
NPC_MISSION_LEGACY_1,
Expand All @@ -160,6 +157,7 @@ enum npc_mission : int {
NPC_MISSION_GUARD_PATROL, // Assigns a non-allied NPC to guard and investigate
NPC_MISSION_ACTIVITY, // Perform a player_activity until it is complete
NPC_MISSION_TRAVELLING,
NPC_MISSION_ASSIGNED_CAMP, // this npc is assigned to a camp.
};

struct npc_companion_mission {
Expand Down
25 changes: 24 additions & 1 deletion src/npcmove.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,29 @@ void npc::move()
mission = NPC_MISSION_NULL;
}
}
if( mission == NPC_MISSION_ASSIGNED_CAMP ) {
bool found_job = false;
if( has_job() && calendar::once_every( 30_minutes ) ) {
if( job_duties.find( job ) != job_duties.end() ) {
const std::vector<activity_id> jobs_to_rotate = job_duties[job];
if( !jobs_to_rotate.empty() ) {
assign_activity( random_entry( jobs_to_rotate ) );
set_mission( NPC_MISSION_ACTIVITY );
set_attitude( NPCATT_ACTIVITY );
action = npc_player_activity;
found_job = true;
} else {
debugmsg( "NPC is assigned to a job, but the job: %s has no duties", npc_job_id( job ) );
set_mission( NPC_MISSION_GUARD_ALLY );
set_attitude( NPCATT_NULL );
}
}
}
if( !found_job ) {
action = npc_pause;
goal = global_omt_location();
}
}
if( is_stationary( true ) ) {
// if we're in a vehicle, stay in the vehicle
if( in_vehicle ) {
Expand Down Expand Up @@ -1923,7 +1946,7 @@ npc_action npc::long_term_goal_action()
{
add_msg( m_debug, "long_term_goal_action()" );

if( mission == NPC_MISSION_SHOPKEEP || mission == NPC_MISSION_SHELTER ) {
if( mission == NPC_MISSION_SHOPKEEP || mission == NPC_MISSION_SHELTER || is_player_ally() ) {
return npc_pause; // Shopkeepers just stay put.
}

Expand Down
1 change: 1 addition & 0 deletions src/npctalk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2507,6 +2507,7 @@ void talk_effect_t::parse_string_effect( const std::string &effect_id, JsonObjec
WRAP( do_butcher ),
WRAP( do_farming ),
WRAP( assign_guard ),
WRAP( assign_camp ),
WRAP( stop_guard ),
WRAP( start_camp ),
WRAP( buy_cow ),
Expand Down
1 change: 1 addition & 0 deletions src/npctalk.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ void revert_activity( npc & );
void goto_location( npc & );
void assign_base( npc & );
void assign_guard( npc & );
void assign_camp( npc & );
void stop_guard( npc & );
void end_conversation( npc & );
void insult_combat( npc & );
Expand Down
Loading

0 comments on commit 126c27b

Please sign in to comment.