Skip to content

Commit f300e69

Browse files
authored
Merge pull request #84269 from robclouth/feature/improved-gamepad-input
2 parents f5bb9a0 + 7bd69e0 commit f300e69

20 files changed

+2019
-632
lines changed

data/raw/keybindings.json

Lines changed: 789 additions & 323 deletions
Large diffs are not rendered by default.

src/action.cpp

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,8 @@ std::string action_ident( action_id act )
372372
return "toggle_prevent_occlusion";
373373
case ACTION_ACTIONMENU:
374374
return "action_menu";
375+
case ACTION_INTERACT:
376+
return "interact";
375377
case ACTION_ITEMACTION:
376378
return "item_action_menu";
377379
case ACTION_SELECT:
@@ -731,11 +733,60 @@ bool can_interact_at( action_id action, map &here, const tripoint_bub_ms &p )
731733
return can_examine_at( here, p, true );
732734
case ACTION_PICKUP:
733735
return can_pickup_at( here, p );
736+
case ACTION_SMASH:
737+
return here.is_bashable( p );
738+
case ACTION_CHAT: {
739+
Creature *c = get_creature_tracker().creature_at( p );
740+
return c != nullptr && !c->is_avatar() && get_avatar().sees( here, p );
741+
}
734742
default:
735743
return false;
736744
}
745+
}
746+
747+
action_id handle_interact( map &here, const tripoint_bub_ms &pos )
748+
{
749+
const input_context ctxt = get_default_mode_input_context();
750+
751+
std::vector<action_id> valid_actions;
752+
static const std::vector<action_id> check_actions = {
753+
ACTION_EXAMINE,
754+
ACTION_PICKUP,
755+
ACTION_OPEN,
756+
ACTION_CLOSE,
757+
ACTION_BUTCHER,
758+
ACTION_CHAT,
759+
ACTION_MOVE_UP,
760+
ACTION_MOVE_DOWN
761+
};
762+
763+
for( action_id act : check_actions ) {
764+
if( can_interact_at( act, here, pos ) ) {
765+
valid_actions.push_back( act );
766+
}
767+
}
768+
769+
if( valid_actions.empty() ) {
770+
return ACTION_NULL;
771+
}
772+
773+
if( valid_actions.size() == 1 ) {
774+
return valid_actions.front();
775+
}
776+
777+
uilist tmenu;
778+
tmenu.settext( _( "Actions for this tile" ) );
779+
for( action_id act : valid_actions ) {
780+
tmenu.addentry( act, true, hotkey_for_action( act, 1 ),
781+
ctxt.get_action_name( action_ident( act ) ) );
782+
}
783+
784+
tmenu.query();
785+
if( tmenu.ret < 0 ) {
786+
return ACTION_NULL;
787+
}
737788

738-
return can_interact_at( action, here, p );
789+
return static_cast<action_id>( tmenu.ret );
739790
}
740791

741792
action_id handle_action_menu( map &here )

src/action.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ enum action_id : int {
314314
ACTION_ZOOM_OUT,
315315
/** Zoom view out */
316316
ACTION_ZOOM_IN,
317-
/** Open the action menu */
317+
/** Open the the action menu */
318318
ACTION_ACTIONMENU,
319319
/** Open the item uses menu */
320320
ACTION_ITEMACTION,
@@ -353,6 +353,8 @@ enum action_id : int {
353353
ACTION_DISPLAY_NPC_ATTACK_POTENTIAL,
354354
/** Toggle timing of the game hours */
355355
ACTION_TOGGLE_HOUR_TIMER,
356+
/** Interact with the current or nearby tile */
357+
ACTION_INTERACT,
356358
/** Not an action, serves as count of enumerated actions */
357359
NUM_ACTIONS
358360
/**@}*/
@@ -603,6 +605,17 @@ point_rel_ms get_delta_from_movement_action( action_id act, iso_rotate rot );
603605
*/
604606
action_id handle_action_menu( map &here );
605607

608+
/**
609+
* Show a context-sensitive action menu for a specific tile.
610+
*
611+
* If only one action is possible, it is returned immediately.
612+
* If multiple actions are possible, a menu is shown.
613+
*
614+
* @param pos The tile to perform actions on.
615+
* @returns action_id ID of action requested by user, or ACTION_NULL.
616+
*/
617+
action_id handle_interact( map &here, const tripoint_bub_ms &pos );
618+
606619
/**
607620
* Show in-game main menu
608621
*

src/cata_tiles.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "flexbuffer_json.h"
4747
#include "game.h"
4848
#include "input.h"
49+
#include "sdl_gamepad.h"
4950
#include "item.h"
5051
#include "item_factory.h"
5152
#include "itype.h"
@@ -1943,6 +1944,18 @@ void cata_tiles::draw( const point &dest, const tripoint_bub_ms &center, int wid
19431944
}
19441945
}
19451946

1947+
// Draw gamepad direction indicator
1948+
if( gamepad::is_active() ) {
1949+
gamepad::direction dir = gamepad::get_left_stick_direction();
1950+
if( dir != gamepad::direction::NONE ) {
1951+
tripoint offset = gamepad::direction_to_offset( dir );
1952+
tripoint_bub_ms indicator_pos = you.pos_bub() + tripoint_rel_ms( offset.x, offset.y, 0 );
1953+
draw_from_id_string( "cursor", TILE_CATEGORY::NONE, empty_string,
1954+
tripoint_bub_ms( indicator_pos.xy(), center.z() ),
1955+
0, 0, lit_level::LIT, false );
1956+
}
1957+
}
1958+
19461959
printErrorIf( SDL_RenderSetClipRect( renderer.get(), nullptr ) != 0,
19471960
"SDL_RenderSetClipRect failed" );
19481961
}

src/game.cpp

Lines changed: 65 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2556,6 +2556,7 @@ input_context get_default_mode_input_context()
25562556
ctxt.register_action( "loot" );
25572557
ctxt.register_action( "examine" );
25582558
ctxt.register_action( "examine_and_pickup" );
2559+
ctxt.register_action( "interact", to_translation( "Interact with tile" ) );
25592560
ctxt.register_action( "advinv" );
25602561
ctxt.register_action( "pickup" );
25612562
ctxt.register_action( "pickup_all" );
@@ -5040,39 +5041,50 @@ void game::moving_vehicle_dismount( const tripoint_bub_ms &dest_loc )
50405041
}
50415042
}
50425043

5043-
void game::control_vehicle()
5044+
void game::control_vehicle( const std::optional<tripoint_bub_ms> &p )
50445045
{
50455046
map &here = get_map();
50465047

5047-
if( vehicle *remote_veh = remoteveh() ) { // remote controls have priority
5048-
for( const vpart_reference &vpr : remote_veh->get_avail_parts( "REMOTE_CONTROLS" ) ) {
5049-
remote_veh->interact_with( &here, vpr.pos_bub( here ) );
5050-
return;
5048+
if( !p && !here.veh_at( u.pos_bub() ) ) {
5049+
if( vehicle *remote_veh = remoteveh() ) { // remote controls have priority
5050+
for( const vpart_reference &vpr : remote_veh->get_avail_parts( "REMOTE_CONTROLS" ) ) {
5051+
remote_veh->interact_with( &here, vpr.pos_bub( here ) );
5052+
return;
5053+
}
50515054
}
50525055
}
5056+
50535057
vehicle *veh = nullptr;
50545058
bool controls_ok = false;
50555059
bool reins_ok = false;
5056-
if( const optional_vpart_position vp = here.veh_at( u.pos_bub() ) ) {
5060+
const tripoint_bub_ms player_pos = u.pos_bub();
5061+
const tripoint_bub_ms target_pos = p.value_or( player_pos );
5062+
5063+
if( const optional_vpart_position vp = here.veh_at( target_pos ) ) {
50575064
veh = &vp->vehicle();
50585065
const int controls_idx = veh->avail_part_with_feature( vp->mount_pos(), "CONTROLS" );
50595066
const int reins_idx = veh->avail_part_with_feature( vp->mount_pos(), "CONTROL_ANIMAL" );
50605067
controls_ok = controls_idx >= 0; // controls available to "drive"
50615068
reins_ok = reins_idx >= 0 // reins + animal available to "drive"
50625069
&& veh->has_engine_type( fuel_type_animal, false )
50635070
&& veh->get_harnessed_animal( here );
5064-
if( veh->player_in_control( here, u ) ) {
5065-
// player already "driving" - offer ways to leave
5066-
if( controls_ok ) {
5067-
veh->interact_with( &here, u.pos_bub() );
5068-
} else if( reins_idx >= 0 ) {
5069-
u.controlling_vehicle = false;
5070-
add_msg( m_info, _( "You let go of the reins." ) );
5071-
}
5072-
} else if( u.in_vehicle && ( controls_ok || reins_ok ) ) {
5073-
// player not driving but has controls or reins on tile
5071+
5072+
if( target_pos == player_pos ) {
5073+
if( veh->player_in_control( here, u ) ) {
5074+
// player already "driving" - offer ways to leave
5075+
if( controls_ok ) {
5076+
veh->interact_with( &here, u.pos_bub() );
5077+
} else if( reins_idx >= 0 ) {
5078+
u.controlling_vehicle = false;
5079+
add_msg( m_info, _( "You let go of the reins." ) );
5080+
}
5081+
return;
5082+
}
5083+
}
5084+
5085+
if( controls_ok || reins_ok ) {
50745086
if( veh->is_locked ) {
5075-
veh->interact_with( &here, u.pos_bub() );
5087+
veh->interact_with( &here, target_pos );
50765088
return; // interact_with offers to hotwire
50775089
}
50785090
if( !veh->handle_potential_theft( u ) ) {
@@ -5112,13 +5124,27 @@ void game::control_vehicle()
51125124
} else {
51135125
veh->start_engines( here, &u, true );
51145126
}
5127+
// Success!
5128+
if( u.controlling_vehicle ) {
5129+
for( const tripoint_abs_ms &target : veh->get_points() ) {
5130+
u.memorize_clear_decoration( target, "vp_" );
5131+
here.memory_cache_dec_set_dirty( here.get_bub( target ), true );
5132+
}
5133+
veh->is_following = false;
5134+
veh->is_patrolling = false;
5135+
veh->autopilot_on = false;
5136+
veh->is_autodriving = false;
5137+
}
5138+
return;
51155139
}
51165140
}
5117-
if( !controls_ok && !reins_ok ) { // no controls or reins under player position, search nearby
5141+
5142+
if( !p ) {
5143+
// Fallback search nearby if no target point provided and no controls at player position
51185144
int num_valid_controls = 0;
51195145
std::optional<tripoint_bub_ms> vehicle_position;
51205146
std::optional<vpart_reference> vehicle_controls;
5121-
for( const tripoint_bub_ms &elem : here.points_in_radius( get_player_character().pos_bub(), 1 ) ) {
5147+
for( const tripoint_bub_ms &elem : here.points_in_radius( u.pos_bub(), 1 ) ) {
51225148
if( const optional_vpart_position vp = here.veh_at( elem ) ) {
51235149
const std::optional<vpart_reference> controls = vp.value().part_with_feature( "CONTROLS", true );
51245150
if( controls ) {
@@ -5149,18 +5175,18 @@ void game::control_vehicle()
51495175
return;
51505176
}
51515177
}
5152-
// If we hit neither of those, there's only one set of vehicle controls, which should already have been found.
51535178
if( vehicle_controls ) {
51545179
veh = &vehicle_controls->vehicle();
51555180
if( !veh->handle_potential_theft( u ) ) {
51565181
return;
51575182
}
51585183
veh->interact_with( &here, *vehicle_position );
51595184
}
5185+
} else {
5186+
add_msg( _( "No vehicle controls found there." ) );
51605187
}
5161-
if( u.controlling_vehicle ) {
5162-
// If we reached here, we gained control of a vehicle.
5163-
// Clear the map memory for the area covered by the vehicle to eliminate ghost vehicles.
5188+
5189+
if( u.controlling_vehicle && veh ) {
51645190
for( const tripoint_abs_ms &target : veh->get_points() ) {
51655191
u.memorize_clear_decoration( target, "vp_" );
51665192
here.memory_cache_dec_set_dirty( here.get_bub( target ), true );
@@ -7605,9 +7631,13 @@ void game::insert_item()
76057631
game_menus::inv::insert_items( item_loc );
76067632
}
76077633

7608-
void game::unload_container()
7634+
void game::unload_container( const std::optional<tripoint_bub_ms> &p )
76097635
{
7610-
if( const std::optional<tripoint_bub_ms> pnt = choose_adjacent( _( "Unload where?" ) ) ) {
7636+
std::optional<tripoint_bub_ms> pnt = p;
7637+
if( !pnt ) {
7638+
pnt = choose_adjacent( _( "Unload where?" ) );
7639+
}
7640+
if( pnt ) {
76117641
u.drop( game_menus::inv::unload_container(), *pnt );
76127642
}
76137643
}
@@ -7723,7 +7753,7 @@ static void add_disassemblables( uilist &menu,
77237753
}
77247754
}
77257755

7726-
void game::butcher()
7756+
void game::butcher( const std::optional<tripoint_bub_ms> &p )
77277757
{
77287758
map &here = get_map();
77297759

@@ -7733,14 +7763,16 @@ void game::butcher()
77337763
return;
77347764
}
77357765

7766+
const tripoint_bub_ms pos = p.value_or( u.pos_bub() );
7767+
77367768
const int factor = u.max_quality( qual_BUTCHER, PICKUP_RANGE );
77377769
const int factorD = u.max_quality( qual_CUT_FINE, PICKUP_RANGE );
77387770
const std::string no_knife_msg = _( "You don't have a butchering tool." );
77397771
const std::string no_corpse_msg = _( "There are no corpses here to butcher." );
77407772

77417773
//You can't butcher on sealed terrain- you have to smash/shovel/etc it open first
7742-
if( here.has_flag( ter_furn_flag::TFLAG_SEALED, u.pos_bub() ) ) {
7743-
if( here.sees_some_items( u.pos_bub(), u ) ) {
7774+
if( here.has_flag( ter_furn_flag::TFLAG_SEALED, pos ) ) {
7775+
if( here.sees_some_items( pos, u ) ) {
77447776
add_msg( m_info, _( "You can't access the items here." ) );
77457777
} else if( factor > INT_MIN || factorD > INT_MIN ) {
77467778
add_msg( m_info, no_corpse_msg );
@@ -7755,7 +7787,7 @@ void game::butcher()
77557787
std::vector<map_stack::iterator> corpses;
77567788
std::vector<map_stack::iterator> disassembles;
77577789
std::vector<map_stack::iterator> salvageables;
7758-
map_stack items = here.i_at( u.pos_bub() );
7790+
map_stack items = here.i_at( pos );
77597791
const inventory &crafting_inv = u.crafting_inventory();
77607792

77617793
// TODO: Properly handle different material whitelists
@@ -7962,7 +7994,7 @@ void game::butcher()
79627994
if( bt.has_value() ) {
79637995
std::vector<butchery_data> bd;
79647996
for( map_stack::iterator &it : corpses ) {
7965-
item_location corpse_loc = item_location( map_cursor( u.pos_abs() ), &*it );
7997+
item_location corpse_loc = item_location( map_cursor( pos ), &*it );
79667998
bd.emplace_back( corpse_loc, bt.value() );
79677999
}
79688000
u.assign_activity( butchery_activity_actor( bd ) );
@@ -7983,7 +8015,7 @@ void game::butcher()
79838015
case BUTCHER_CORPSE: {
79848016
const std::optional<butcher_type> bt = butcher_submenu( corpses, indexer_index );
79858017
if( bt.has_value() ) {
7986-
item_location corpse_loc = item_location( map_cursor( u.pos_abs() ), &*corpses[indexer_index] );
8018+
item_location corpse_loc = item_location( map_cursor( pos ), &*corpses[indexer_index] );
79878019
butchery_data bd( corpse_loc, bt.value() );
79888020
u.assign_activity( butchery_activity_actor( bd ) );
79898021
}
@@ -7992,7 +8024,7 @@ void game::butcher()
79928024
case BUTCHER_DISASSEMBLE: {
79938025
// Pick index of first item in the disassembly stack
79948026
item *const target = &*disassembly_stacks[indexer_index].first;
7995-
u.disassemble( item_location( map_cursor( u.pos_abs() ), target ), true );
8027+
u.disassemble( item_location( map_cursor( pos ), target ), true );
79968028
}
79978029
break;
79988030
case BUTCHER_SALVAGE: {
@@ -8001,7 +8033,7 @@ void game::butcher()
80018033
} else {
80028034
// Pick index of first item in the salvage stack
80038035
item *const target = &*salvage_stacks[indexer_index].first;
8004-
item_location item_loc( map_cursor( u.pos_abs() ), target );
8036+
item_location item_loc( map_cursor( pos ), target );
80058037
salvage_iuse->try_to_cut_up( u, *salvage_tool, item_loc );
80068038
}
80078039
}

src/game.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -961,7 +961,8 @@ class game
961961
bool grabbed_move( const tripoint_rel_ms &dp, bool via_ramp );
962962
bool grabbed_veh_move( const tripoint_rel_ms &dp );
963963

964-
void control_vehicle(); // Use vehicle controls '^'
964+
void control_vehicle( const std::optional<tripoint_bub_ms> &p =
965+
std::nullopt ); // Use vehicle controls '^'
965966
// Examine nearby terrain 'e', with or without picking up items
966967
void examine( const tripoint_bub_ms &p, bool with_pickup = false );
967968
void examine( bool with_pickup = true );
@@ -972,10 +973,11 @@ class game
972973
// Pick up items from all nearby tiles
973974
void pickup_all();
974975

975-
void unload_container(); // Unload a container w/ direction 'd'
976+
void unload_container( const std::optional<tripoint_bub_ms> &p =
977+
std::nullopt ); // Unload a container w/ direction 'd'
976978
void drop_in_direction( const tripoint_bub_ms &pnt ); // Drop w/ direction 'D'
977979

978-
void butcher(); // Butcher a corpse 'B'
980+
void butcher( const std::optional<tripoint_bub_ms> &p = std::nullopt ); // Butcher a corpse 'B'
979981

980982
void reload( item_location &loc, bool prompt = false, bool empty = true );
981983
public:
@@ -1016,10 +1018,9 @@ class game
10161018
std::vector<std::string> *harmful_stuff = nullptr ) const;
10171019
// Pick up items from the given point
10181020
void pickup( const tripoint_bub_ms &p );
1021+
void chat( const std::optional<tripoint_bub_ms> &p = std::nullopt ); // Talk to a nearby NPC 'C'
10191022
private:
10201023

1021-
void chat(); // Talk to a nearby NPC 'C'
1022-
10231024
// Internal methods to show "look around" info
10241025
void print_fields_info( const tripoint_bub_ms &lp, const catacurses::window &w_look, int column,
10251026
int &line );

0 commit comments

Comments
 (0)