Skip to content

Commit

Permalink
Take control of NPC followers
Browse files Browse the repository at this point in the history
  • Loading branch information
eltank committed Sep 9, 2021
1 parent 2d4a487 commit ac174c4
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 4 deletions.
37 changes: 37 additions & 0 deletions src/avatar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,43 @@ avatar::avatar( avatar && ) = default;
// NOLINTNEXTLINE(performance-noexcept-move-constructor)
avatar &avatar::operator=( avatar && ) = default;

static void swap_npc( npc &one, npc &two, npc &tmp )
{
tmp = std::move( one );
one = std::move( two );
two = std::move( tmp );
}

void avatar::control_npc( npc &np )
{
if( !np.is_player_ally() ) {
debugmsg( "control_npc() called on non-allied npc %s", np.name );
return;
}
if( !shadow_npc ) {
shadow_npc = std::make_unique<npc>();
shadow_npc->op_of_u.trust = 10;
shadow_npc->op_of_u.value = 10;
shadow_npc->set_attitude( NPCATT_FOLLOW );
}
npc tmp;
// move avatar character data into shadow npc
swap_character( *shadow_npc, tmp );
// swap target npc with shadow npc
swap_npc( *shadow_npc, np, tmp );
// move shadow npc character data into avatar
swap_character( *shadow_npc, tmp );
// the avatar character is no longer a follower NPC
g->remove_npc_follower( getID() );
// the previous avatar character is now a follower
g->add_npc_follower( np.getID() );
np.set_fac( faction_id( "your_followers" ) );
// perception and mutations may have changed, so reset light level caches
g->reset_light_level();
// center the map on the new avatar character
g->update_map( *this );
}

void avatar::toggle_map_memory()
{
show_map_memory = !show_map_memory;
Expand Down
12 changes: 12 additions & 0 deletions src/avatar.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ class avatar : public Character
return this;
}

/**
* Makes the avatar "take over" the given NPC, while the current avatar character
* becomes an NPC.
*/
void control_npc( npc & );
using Character::query_yn;
bool query_yn( const std::string &mes ) const override;

Expand Down Expand Up @@ -316,6 +321,7 @@ class avatar : public Character

vproto_id starting_vehicle;
std::vector<mtype_id> starting_pets;
std::set<character_id> follower_ids;

private:
// the encumbrance on your limbs reducing your dodging ability
Expand Down Expand Up @@ -361,6 +367,12 @@ class avatar : public Character
int per_upgrade = 0;

monster_visible_info mon_visible;

/**
* The NPC that would control the avatar's character in the avatar's absence.
* The Character data in this object is not relevant/used.
*/
std::unique_ptr<npc> shadow_npc;
};

avatar &get_avatar();
Expand Down
7 changes: 7 additions & 0 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,13 @@ character_id Character::getID() const
return this->id;
}

void Character::swap_character( Character &other, Character &tmp )
{
tmp = std::move( other );
other = std::move( *this );
*this = std::move( tmp );
}

void Character::randomize_height()
{
// Height distribution data is taken from CDC distributes statistics for the US population
Expand Down
4 changes: 3 additions & 1 deletion src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -2247,7 +2247,6 @@ class Character : public Creature, public visitable
int focus_pool = 0;
public:
int cash = 0;
std::set<character_id> follower_ids;
weak_ptr_fast<Creature> last_target;
cata::optional<tripoint> last_target_pos;
// Save favorite ammo location
Expand Down Expand Up @@ -3066,6 +3065,9 @@ class Character : public Creature, public visitable
Character();
Character( Character && ) noexcept( map_is_noexcept );
Character &operator=( Character && ) noexcept( list_is_noexcept );
// Swaps the data of this Character and "other" using "tmp" for temporary storage.
// Leaves "tmp" in an undefined state.
void swap_character( Character &other, Character &tmp );
public:
struct trait_data {
/** Whether the mutation is activated. */
Expand Down
31 changes: 30 additions & 1 deletion src/debug_menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ std::string enum_to_string<debug_menu::debug_menu_index>( debug_menu::debug_menu
case debug_menu::debug_menu_index::LEARN_MA: return "LEARN_MA";
case debug_menu::debug_menu_index::UNLOCK_RECIPES: return "UNLOCK_RECIPES";
case debug_menu::debug_menu_index::EDIT_PLAYER: return "EDIT_PLAYER";
case debug_menu::debug_menu_index::CONTROL_NPC: return "CONTROL_NPC";
case debug_menu::debug_menu_index::SPAWN_ARTIFACT: return "SPAWN_ARTIFACT";
case debug_menu::debug_menu_index::SPAWN_CLAIRVOYANCE: return "SPAWN_CLAIRVOYANCE";
case debug_menu::debug_menu_index::MAP_EDITOR: return "MAP_EDITOR";
Expand Down Expand Up @@ -237,6 +238,7 @@ static int player_uilist()
{ uilist_entry( debug_menu_index::DAMAGE_SELF, true, 'd', _( "Damage self" ) ) },
{ uilist_entry( debug_menu_index::BLEED_SELF, true, 'b', _( "Bleed self" ) ) },
{ uilist_entry( debug_menu_index::SET_AUTOMOVE, true, 'a', _( "Set automove route" ) ) },
{ uilist_entry( debug_menu_index::CONTROL_NPC, true, 'x', _( "Control NPC follower" ) ) },
};
if( !spell_type::get_all().empty() ) {
uilist_initializer.emplace_back( uilist_entry( debug_menu_index::CHANGE_SPELLS, true, 'S',
Expand Down Expand Up @@ -1126,6 +1128,29 @@ static void spawn_nested_mapgen()
}
}

static void control_npc_menu()
{
std::vector<shared_ptr_fast<npc>> followers;
uilist charmenu;
int charnum = 0;
for( const auto &elem : g->get_follower_list() ) {
shared_ptr_fast<npc> follower = overmap_buffer.find_npc( elem );
if( follower ) {
followers.emplace_back( follower );
charmenu.addentry( charnum++, true, MENU_AUTOASSIGN, follower->get_name() );
}
}
if( followers.empty() ) {
return;
}
charmenu.w_y_setup = 0;
charmenu.query();
if( charmenu.ret < 0 || static_cast<size_t>( charmenu.ret ) >= followers.size() ) {
return;
}
get_avatar().control_npc( *followers.at( charmenu.ret ) );
}

static void character_edit_menu()
{
std::vector< tripoint > locations;
Expand Down Expand Up @@ -2361,7 +2386,11 @@ void debug()
break;

case debug_menu_index::EDIT_PLAYER:
debug_menu::character_edit_menu();
character_edit_menu();
break;

case debug_menu_index::CONTROL_NPC:
control_npc_menu();
break;

case debug_menu_index::SPAWN_ARTIFACT:
Expand Down
1 change: 1 addition & 0 deletions src/debug_menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ enum class debug_menu_index : int {
LEARN_MA,
UNLOCK_RECIPES,
EDIT_PLAYER,
CONTROL_NPC,
SPAWN_ARTIFACT,
SPAWN_CLAIRVOYANCE,
MAP_EDITOR,
Expand Down
4 changes: 2 additions & 2 deletions src/savegame_json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -963,7 +963,6 @@ void Character::load( const JsonObject &data )
}

data.read( "addictions", addictions );
data.read( "followers", follower_ids );

// Add the earplugs.
if( has_bionic( bionic_id( "bio_ears" ) ) && !has_bionic( bionic_id( "bio_earplugs" ) ) ) {
Expand Down Expand Up @@ -1174,7 +1173,6 @@ void Character::store( JsonOut &json ) const

// "Looks like I picked the wrong week to quit smoking." - Steve McCroskey
json.member( "addictions", addictions );
json.member( "followers", follower_ids );

json.member( "worn", worn ); // also saves contents
json.member( "inv" );
Expand Down Expand Up @@ -1257,6 +1255,7 @@ void avatar::store( JsonOut &json ) const
json.end_array();
}

json.member( "followers", follower_ids );
// someday, npcs may drive
json.member( "controlling_vehicle", controlling_vehicle );

Expand Down Expand Up @@ -1346,6 +1345,7 @@ void avatar::load( const JsonObject &data )
hobbies.insert( hobbies.end(), &hobby.obj() );
}

data.read( "followers", follower_ids );
data.read( "controlling_vehicle", controlling_vehicle );

data.read( "grab_point", grab_point );
Expand Down

0 comments on commit ac174c4

Please sign in to comment.