1 #include "action.h"
2 
3 #include <algorithm>
4 #include <climits>
5 #include <istream>
6 #include <iterator>
7 #include <memory>
8 #include <new>
9 #include <string>
10 #include <utility>
11 
12 #include "avatar.h"
13 #include "cached_options.h"
14 #include "cata_utility.h"
15 #include "character.h"
16 #include "colony.h"
17 #include "creature.h"
18 #include "debug.h"
19 #include "flag.h"
20 #include "game.h"
21 #include "input.h"
22 #include "inventory.h"
23 #include "item.h"
24 #include "map.h"
25 #include "map_iterator.h"
26 #include "mapdata.h"
27 #include "memory_fast.h"
28 #include "messages.h"
29 #include "optional.h"
30 #include "options.h"
31 #include "output.h"
32 #include "path_info.h"
33 #include "point.h"
34 #include "popup.h"
35 #include "ret_val.h"
36 #include "translations.h"
37 #include "type_id.h"
38 #include "ui.h"
39 #include "ui_manager.h"
40 #include "vehicle.h"
41 #include "vpart_position.h"
42 
43 static const quality_id qual_BUTCHER( "BUTCHER" );
44 static const quality_id qual_CUT_FINE( "CUT_FINE" );
45 
46 static const std::string flag_CONSOLE( "CONSOLE" );
47 static const std::string flag_GOES_DOWN( "GOES_DOWN" );
48 static const std::string flag_GOES_UP( "GOES_UP" );
49 static const std::string flag_SWIMMABLE( "SWIMMABLE" );
50 
51 static void parse_keymap( std::istream &keymap_txt, std::map<char, action_id> &kmap,
52                           std::set<action_id> &unbound_keymap );
53 
load_keyboard_settings(std::map<char,action_id> & keymap,std::string & keymap_file_loaded_from,std::set<action_id> & unbound_keymap)54 void load_keyboard_settings( std::map<char, action_id> &keymap,
55                              std::string &keymap_file_loaded_from,
56                              std::set<action_id> &unbound_keymap )
57 {
58     const auto parser = [&]( std::istream & fin ) {
59         parse_keymap( fin, keymap, unbound_keymap );
60     };
61     if( read_from_file_optional( PATH_INFO::keymap(), parser ) ) {
62         keymap_file_loaded_from = PATH_INFO::keymap();
63     }
64 }
65 
parse_keymap(std::istream & keymap_txt,std::map<char,action_id> & kmap,std::set<action_id> & unbound_keymap)66 void parse_keymap( std::istream &keymap_txt, std::map<char, action_id> &kmap,
67                    std::set<action_id> &unbound_keymap )
68 {
69     while( !keymap_txt.eof() ) {
70         std::string id;
71         keymap_txt >> id;
72         if( id.empty() ) {
73             // Empty line, chomp it
74             getline( keymap_txt, id );
75         } else if( id == "unbind" ) {
76             keymap_txt >> id;
77             const action_id act = look_up_action( id );
78             if( act != ACTION_NULL ) {
79                 unbound_keymap.insert( act );
80             }
81             break;
82         } else if( id[0] != '#' ) {
83             const action_id act = look_up_action( id );
84             if( act == ACTION_NULL ) {
85                 debugmsg( "Warning!  keymap.txt contains an unknown action, \"%s\"\n"
86                           "Fix \"%s\" at your next chance!", id, PATH_INFO::keymap() );
87             } else {
88                 while( !keymap_txt.eof() ) {
89                     char ch;
90                     keymap_txt >> std::noskipws >> ch >> std::skipws;
91                     if( ch == '\n' ) {
92                         break;
93                     } else if( ch != ' ' || keymap_txt.peek() == '\n' ) {
94                         if( kmap.find( ch ) != kmap.end() ) {
95                             debugmsg( "Warning!  '%c' assigned twice in the keymap!\n"
96                                       "%s is being ignored.\n"
97                                       "Fix \"%s\" at your next chance!", ch, id, PATH_INFO::keymap() );
98                         } else {
99                             kmap[ ch ] = act;
100                         }
101                     }
102                 }
103             }
104         } else {
105             // Clear the whole line
106             getline( keymap_txt, id );
107         }
108     }
109 }
110 
keys_bound_to(const action_id act,const int maximum_modifier_count,const bool restrict_to_printable)111 std::vector<input_event> keys_bound_to( const action_id act,
112                                         const int maximum_modifier_count,
113                                         const bool restrict_to_printable )
114 {
115     input_context ctxt = get_default_mode_input_context();
116     return ctxt.keys_bound_to( action_ident( act ), maximum_modifier_count,
117                                restrict_to_printable );
118 }
119 
action_ident(action_id act)120 std::string action_ident( action_id act )
121 {
122     switch( act ) {
123         case ACTION_PAUSE:
124             return "pause";
125         case ACTION_TIMEOUT:
126             return "TIMEOUT";
127         case ACTION_MOVE_FORTH:
128             return "UP";
129         case ACTION_MOVE_FORTH_RIGHT:
130             return "RIGHTUP";
131         case ACTION_MOVE_RIGHT:
132             return "RIGHT";
133         case ACTION_MOVE_BACK_RIGHT:
134             return "RIGHTDOWN";
135         case ACTION_MOVE_BACK:
136             return "DOWN";
137         case ACTION_MOVE_BACK_LEFT:
138             return "LEFTDOWN";
139         case ACTION_MOVE_LEFT:
140             return "LEFT";
141         case ACTION_MOVE_FORTH_LEFT:
142             return "LEFTUP";
143         case ACTION_MOVE_DOWN:
144             return "LEVEL_DOWN";
145         case ACTION_MOVE_UP:
146             return "LEVEL_UP";
147         case ACTION_TOGGLE_MAP_MEMORY:
148             return "toggle_map_memory";
149         case ACTION_CENTER:
150             return "center";
151         case ACTION_SHIFT_N:
152             return "shift_n";
153         case ACTION_SHIFT_NE:
154             return "shift_ne";
155         case ACTION_SHIFT_E:
156             return "shift_e";
157         case ACTION_SHIFT_SE:
158             return "shift_se";
159         case ACTION_SHIFT_S:
160             return "shift_s";
161         case ACTION_SHIFT_SW:
162             return "shift_sw";
163         case ACTION_SHIFT_W:
164             return "shift_w";
165         case ACTION_SHIFT_NW:
166             return "shift_nw";
167         case ACTION_CYCLE_MOVE:
168             return "cycle_move";
169         case ACTION_RESET_MOVE:
170             return "reset_move";
171         case ACTION_TOGGLE_RUN:
172             return "toggle_run";
173         case ACTION_TOGGLE_CROUCH:
174             return "toggle_crouch";
175         case ACTION_OPEN_MOVEMENT:
176             return "open_movement";
177         case ACTION_OPEN:
178             return "open";
179         case ACTION_CLOSE:
180             return "close";
181         case ACTION_SMASH:
182             return "smash";
183         case ACTION_EXAMINE:
184             return "examine";
185         case ACTION_ADVANCEDINV:
186             return "advinv";
187         case ACTION_PICKUP:
188             return "pickup";
189         case ACTION_PICKUP_FEET:
190             return "pickup_feet";
191         case ACTION_GRAB:
192             return "grab";
193         case ACTION_HAUL:
194             return "haul";
195         case ACTION_BUTCHER:
196             return "butcher";
197         case ACTION_CHAT:
198             return "chat";
199         case ACTION_LOOK:
200             return "look";
201         case ACTION_PEEK:
202             return "peek";
203         case ACTION_LIST_ITEMS:
204             return "listitems";
205         case ACTION_ZONES:
206             return "zones";
207         case ACTION_LOOT:
208             return "loot";
209         case ACTION_INVENTORY:
210             return "inventory";
211         case ACTION_COMPARE:
212             return "compare";
213         case ACTION_ORGANIZE:
214             return "organize";
215         case ACTION_USE:
216             return "apply";
217         case ACTION_USE_WIELDED:
218             return "apply_wielded";
219         case ACTION_WEAR:
220             return "wear";
221         case ACTION_TAKE_OFF:
222             return "take_off";
223         case ACTION_EAT:
224             return "eat";
225         case ACTION_OPEN_CONSUME:
226             return "open_consume";
227         case ACTION_READ:
228             return "read";
229         case ACTION_WIELD:
230             return "wield";
231         case ACTION_PICK_STYLE:
232             return "pick_style";
233         case ACTION_RELOAD_ITEM:
234             return "reload_item";
235         case ACTION_RELOAD_WEAPON:
236             return "reload_weapon";
237         case ACTION_RELOAD_WIELDED:
238             return "reload_wielded";
239         case ACTION_UNLOAD:
240             return "unload";
241         case ACTION_MEND:
242             return "mend";
243         case ACTION_THROW:
244             return "throw";
245         case ACTION_FIRE:
246             return "fire";
247         case ACTION_FIRE_BURST:
248             return "fire_burst";
249         case ACTION_CAST_SPELL:
250             return "cast_spell";
251         case ACTION_SELECT_FIRE_MODE:
252             return "select_fire_mode";
253         case ACTION_DROP:
254             return "drop";
255         case ACTION_DIR_DROP:
256             return "drop_adj";
257         case ACTION_BIONICS:
258             return "bionics";
259         case ACTION_MUTATIONS:
260             return "mutations";
261         case ACTION_SORT_ARMOR:
262             return "sort_armor";
263         case ACTION_WAIT:
264             return "wait";
265         case ACTION_CRAFT:
266             return "craft";
267         case ACTION_RECRAFT:
268             return "recraft";
269         case ACTION_LONGCRAFT:
270             return "long_craft";
271         case ACTION_CONSTRUCT:
272             return "construct";
273         case ACTION_DISASSEMBLE:
274             return "disassemble";
275         case ACTION_SLEEP:
276             return "sleep";
277         case ACTION_CONTROL_VEHICLE:
278             return "control_vehicle";
279         case ACTION_TOGGLE_AUTO_TRAVEL_MODE:
280             return "auto_travel_mode";
281         case ACTION_TOGGLE_SAFEMODE:
282             return "safemode";
283         case ACTION_TOGGLE_AUTOSAFE:
284             return "autosafe";
285         case ACTION_TOGGLE_THIEF_MODE:
286             return "toggle_thief_mode";
287         case ACTION_IGNORE_ENEMY:
288             return "ignore_enemy";
289         case ACTION_WHITELIST_ENEMY:
290             return "whitelist_enemy";
291         case ACTION_WORKOUT:
292             return "workout";
293         case ACTION_SAVE:
294             return "save";
295         case ACTION_QUICKSAVE:
296             return "quicksave";
297         case ACTION_QUICKLOAD:
298             return "quickload";
299         case ACTION_SUICIDE:
300             return "SUICIDE";
301         case ACTION_PL_INFO:
302             return "player_data";
303         case ACTION_MAP:
304             return "map";
305         case ACTION_SKY:
306             return "sky";
307         case ACTION_MISSIONS:
308             return "missions";
309         case ACTION_FACTIONS:
310             return "factions";
311         case ACTION_SCORES:
312             return "scores";
313         case ACTION_MORALE:
314             return "morale";
315         case ACTION_MESSAGES:
316             return "messages";
317         case ACTION_HELP:
318             return "help";
319         case ACTION_DEBUG:
320             return "debug";
321         case ACTION_DISPLAY_SCENT:
322             return "debug_scent";
323         case ACTION_DISPLAY_SCENT_TYPE:
324             return "debug_scent_type";
325         case ACTION_DISPLAY_TEMPERATURE:
326             return "debug_temp";
327         case ACTION_DISPLAY_VEHICLE_AI:
328             return "debug_vehicle_ai";
329         case ACTION_DISPLAY_VISIBILITY:
330             return "debug_visibility";
331         case ACTION_DISPLAY_TRANSPARENCY:
332             return "debug_transparency";
333         case ACTION_DISPLAY_REACHABILITY_ZONES:
334             return "display_reachability_zones";
335         case ACTION_DISPLAY_LIGHTING:
336             return "debug_lighting";
337         case ACTION_DISPLAY_RADIATION:
338             return "debug_radiation";
339         case ACTION_TOGGLE_HOUR_TIMER:
340             return "debug_hour_timer";
341         case ACTION_TOGGLE_DEBUG_MODE:
342             return "debug_mode";
343         case ACTION_ZOOM_OUT:
344             return "zoom_out";
345         case ACTION_ZOOM_IN:
346             return "zoom_in";
347         case ACTION_TOGGLE_FULLSCREEN:
348             return "toggle_fullscreen";
349         case ACTION_TOGGLE_PIXEL_MINIMAP:
350             return "toggle_pixel_minimap";
351         case ACTION_TOGGLE_PANEL_ADM:
352             return "toggle_panel_adm";
353         case ACTION_PANEL_MGMT:
354             return "panel_mgmt";
355         case ACTION_RELOAD_TILESET:
356             return "reload_tileset";
357         case ACTION_TOGGLE_AUTO_FEATURES:
358             return "toggle_auto_features";
359         case ACTION_TOGGLE_AUTO_PULP_BUTCHER:
360             return "toggle_auto_pulp_butcher";
361         case ACTION_TOGGLE_AUTO_MINING:
362             return "toggle_auto_mining";
363         case ACTION_TOGGLE_AUTO_FORAGING:
364             return "toggle_auto_foraging";
365         case ACTION_TOGGLE_AUTO_PICKUP:
366             return "toggle_auto_pickup";
367         case ACTION_ACTIONMENU:
368             return "action_menu";
369         case ACTION_ITEMACTION:
370             return "item_action_menu";
371         case ACTION_SELECT:
372             return "SELECT";
373         case ACTION_SEC_SELECT:
374             return "SEC_SELECT";
375         case ACTION_AUTOATTACK:
376             return "autoattack";
377         case ACTION_MAIN_MENU:
378             return "main_menu";
379         case ACTION_KEYBINDINGS:
380             return "HELP_KEYBINDINGS";
381         case ACTION_OPTIONS:
382             return "open_options";
383         case ACTION_AUTOPICKUP:
384             return "open_autopickup";
385         case ACTION_AUTONOTES:
386             return "open_autonotes";
387         case ACTION_SAFEMODE:
388             return "open_safemode";
389         case ACTION_COLOR:
390             return "open_color";
391         case ACTION_WORLD_MODS:
392             return "open_world_mods";
393         case ACTION_NULL:
394             return "null";
395         default:
396             return "unknown";
397     }
398 }
399 
can_action_change_worldstate(const action_id act)400 bool can_action_change_worldstate( const action_id act )
401 {
402     switch( act ) {
403         // Shift view
404         case ACTION_TOGGLE_MAP_MEMORY:
405         case ACTION_CENTER:
406         case ACTION_SHIFT_N:
407         case ACTION_SHIFT_NE:
408         case ACTION_SHIFT_E:
409         case ACTION_SHIFT_SE:
410         case ACTION_SHIFT_S:
411         case ACTION_SHIFT_SW:
412         case ACTION_SHIFT_W:
413         case ACTION_SHIFT_NW:
414         // Environment Interaction
415         case ACTION_LOOK:
416         case ACTION_LIST_ITEMS:
417         case ACTION_ZONES:
418         // Long-term / special actions
419         case ACTION_SAVE:
420         case ACTION_QUICKSAVE:
421         case ACTION_QUICKLOAD:
422         case ACTION_SUICIDE:
423         // Info Screens
424         case ACTION_PL_INFO:
425         case ACTION_MAP:
426         case ACTION_SKY:
427         case ACTION_MISSIONS:
428         case ACTION_SCORES:
429         case ACTION_FACTIONS:
430         case ACTION_MORALE:
431         case ACTION_MESSAGES:
432         case ACTION_HELP:
433         case ACTION_MAIN_MENU:
434         case ACTION_KEYBINDINGS:
435         case ACTION_OPTIONS:
436         case ACTION_AUTOPICKUP:
437         case ACTION_AUTONOTES:
438         case ACTION_SAFEMODE:
439         case ACTION_COLOR:
440         case ACTION_WORLD_MODS:
441         // Debug Functions
442         case ACTION_TOGGLE_FULLSCREEN:
443         case ACTION_DEBUG:
444         case ACTION_DISPLAY_SCENT:
445         case ACTION_DISPLAY_SCENT_TYPE:
446         case ACTION_DISPLAY_TEMPERATURE:
447         case ACTION_DISPLAY_VEHICLE_AI:
448         case ACTION_DISPLAY_VISIBILITY:
449         case ACTION_DISPLAY_LIGHTING:
450         case ACTION_DISPLAY_RADIATION:
451         case ACTION_DISPLAY_TRANSPARENCY:
452         case ACTION_DISPLAY_REACHABILITY_ZONES:
453         case ACTION_ZOOM_OUT:
454         case ACTION_ZOOM_IN:
455         case ACTION_TOGGLE_PIXEL_MINIMAP:
456         case ACTION_TOGGLE_PANEL_ADM:
457         case ACTION_PANEL_MGMT:
458         case ACTION_RELOAD_TILESET:
459         case ACTION_TIMEOUT:
460         case ACTION_TOGGLE_AUTO_FEATURES:
461         case ACTION_TOGGLE_AUTO_PULP_BUTCHER:
462         case ACTION_TOGGLE_AUTO_MINING:
463         case ACTION_TOGGLE_AUTO_FORAGING:
464             return false;
465         default:
466             return true;
467     }
468 }
469 
look_up_action(const std::string & ident)470 action_id look_up_action( const std::string &ident )
471 {
472     // Temporarily for the interface with the input manager!
473     if( ident == "move_nw" ) {
474         return ACTION_MOVE_FORTH_LEFT;
475     } else if( ident == "move_sw" ) {
476         return ACTION_MOVE_BACK_LEFT;
477     } else if( ident == "move_ne" ) {
478         return ACTION_MOVE_FORTH_RIGHT;
479     } else if( ident == "move_se" ) {
480         return ACTION_MOVE_BACK_RIGHT;
481     } else if( ident == "move_n" ) {
482         return ACTION_MOVE_FORTH;
483     } else if( ident == "move_s" ) {
484         return ACTION_MOVE_BACK;
485     } else if( ident == "move_w" ) {
486         return ACTION_MOVE_LEFT;
487     } else if( ident == "move_e" ) {
488         return ACTION_MOVE_RIGHT;
489     } else if( ident == "move_down" ) {
490         return ACTION_MOVE_DOWN;
491     } else if( ident == "move_up" ) {
492         return ACTION_MOVE_UP;
493     }
494     // ^^ Temporarily for the interface with the input manager!
495     for( int i = 0; i < NUM_ACTIONS; i++ ) {
496         if( action_ident( static_cast<action_id>( i ) ) == ident ) {
497             return static_cast<action_id>( i );
498         }
499     }
500     return ACTION_NULL;
501 }
502 
503 // (Press X (or Y)|Try) to Z
press_x(action_id act)504 std::string press_x( action_id act )
505 {
506     input_context ctxt = get_default_mode_input_context();
507     return ctxt.press_x( action_ident( act ), _( "Press " ), "", _( "Try" ) );
508 }
press_x(action_id act,const std::string & key_bound,const std::string & key_unbound)509 std::string press_x( action_id act, const std::string &key_bound, const std::string &key_unbound )
510 {
511     input_context ctxt = get_default_mode_input_context();
512     return ctxt.press_x( action_ident( act ), key_bound, "", key_unbound );
513 }
press_x(action_id act,const std::string & key_bound_pre,const std::string & key_bound_suf,const std::string & key_unbound)514 std::string press_x( action_id act, const std::string &key_bound_pre,
515                      const std::string &key_bound_suf,
516                      const std::string &key_unbound )
517 {
518     input_context ctxt = get_default_mode_input_context();
519     return ctxt.press_x( action_ident( act ), key_bound_pre, key_bound_suf, key_unbound );
520 }
press_x_if_bound(action_id act)521 cata::optional<std::string> press_x_if_bound( action_id act )
522 {
523     input_context ctxt = get_default_mode_input_context();
524     std::string description = action_ident( act );
525     if( ctxt.keys_bound_to( description, /*maximum_modifier_count=*/ -1,
526                             /*restrict_to_printable=*/false ).empty() ) {
527         return cata::nullopt;
528     }
529     return press_x( act );
530 }
531 
get_movement_action_from_delta(const tripoint & d,const iso_rotate rot)532 action_id get_movement_action_from_delta( const tripoint &d, const iso_rotate rot )
533 {
534     if( d.z == -1 ) {
535         return ACTION_MOVE_DOWN;
536     } else if( d.z == 1 ) {
537         return ACTION_MOVE_UP;
538     }
539 
540     const bool iso_mode = rot == iso_rotate::yes && use_tiles && tile_iso;
541     if( d.xy() == point_north ) {
542         return iso_mode ? ACTION_MOVE_FORTH_LEFT : ACTION_MOVE_FORTH;
543     } else if( d.xy() == point_north_east ) {
544         return iso_mode ? ACTION_MOVE_FORTH : ACTION_MOVE_FORTH_RIGHT;
545     } else if( d.xy() == point_east ) {
546         return iso_mode ? ACTION_MOVE_FORTH_RIGHT : ACTION_MOVE_RIGHT;
547     } else if( d.xy() == point_south_east ) {
548         return iso_mode ? ACTION_MOVE_RIGHT : ACTION_MOVE_BACK_RIGHT;
549     } else if( d.xy() == point_south ) {
550         return iso_mode ? ACTION_MOVE_BACK_RIGHT : ACTION_MOVE_BACK;
551     } else if( d.xy() == point_south_west ) {
552         return iso_mode ? ACTION_MOVE_BACK : ACTION_MOVE_BACK_LEFT;
553     } else if( d.xy() == point_west ) {
554         return iso_mode ? ACTION_MOVE_BACK_LEFT : ACTION_MOVE_LEFT;
555     } else {
556         return iso_mode ? ACTION_MOVE_LEFT : ACTION_MOVE_FORTH_LEFT;
557     }
558 }
559 
get_delta_from_movement_action(const action_id act,const iso_rotate rot)560 point get_delta_from_movement_action( const action_id act, const iso_rotate rot )
561 {
562     const bool iso_mode = rot == iso_rotate::yes && use_tiles && tile_iso;
563     switch( act ) {
564         case ACTION_MOVE_FORTH:
565             return iso_mode ? point_north_east : point_north;
566         case ACTION_MOVE_FORTH_RIGHT:
567             return iso_mode ? point_east : point_north_east;
568         case ACTION_MOVE_RIGHT:
569             return iso_mode ? point_south_east : point_east;
570         case ACTION_MOVE_BACK_RIGHT:
571             return iso_mode ? point_south : point_south_east;
572         case ACTION_MOVE_BACK:
573             return iso_mode ? point_south_west : point_south;
574         case ACTION_MOVE_BACK_LEFT:
575             return iso_mode ? point_west : point_south_west;
576         case ACTION_MOVE_LEFT:
577             return iso_mode ? point_north_west : point_west;
578         case ACTION_MOVE_FORTH_LEFT:
579             return iso_mode ? point_north : point_north_west;
580         default:
581             return point_zero;
582     }
583 }
584 
hotkey_for_action(const action_id action,const int maximum_modifier_count,const bool restrict_to_printable)585 cata::optional<input_event> hotkey_for_action( const action_id action,
586         const int maximum_modifier_count, const bool restrict_to_printable )
587 {
588     const std::vector<input_event> keys = keys_bound_to( action,
589                                           maximum_modifier_count,
590                                           restrict_to_printable );
591     if( keys.empty() ) {
592         return cata::nullopt;
593     } else {
594         return keys.front();
595     }
596 }
597 
can_butcher_at(const tripoint & p)598 bool can_butcher_at( const tripoint &p )
599 {
600     Character &player_character = get_player_character();
601     // TODO: unify this with game::butcher
602     const int factor = player_character.max_quality( qual_BUTCHER );
603     const int factorD = player_character.max_quality( qual_CUT_FINE );
604     map_stack items = get_map().i_at( p );
605     bool has_item = false;
606     bool has_corpse = false;
607 
608     const inventory &crafting_inv = player_character.crafting_inventory();
609     for( item &items_it : items ) {
610         if( items_it.is_corpse() ) {
611             if( factor != INT_MIN  || factorD != INT_MIN ) {
612                 has_corpse = true;
613             }
614         } else if( player_character.can_disassemble( items_it, crafting_inv ).success() ) {
615             has_item = true;
616         }
617     }
618     return has_corpse || has_item;
619 }
620 
can_move_vertical_at(const tripoint & p,int movez)621 bool can_move_vertical_at( const tripoint &p, int movez )
622 {
623     Character &player_character = get_player_character();
624     map &here = get_map();
625     // TODO: unify this with game::move_vertical
626     if( here.has_flag( flag_SWIMMABLE, p ) && here.has_flag( TFLAG_DEEP_WATER, p ) ) {
627         if( movez == -1 ) {
628             return !player_character.is_underwater() && !player_character.worn_with_flag( flag_FLOTATION );
629         } else {
630             return player_character.swim_speed() < 500 ||
631                    player_character.is_wearing( itype_id( "swim_fins" ) );
632         }
633     }
634 
635     if( movez == -1 ) {
636         return here.has_flag( flag_GOES_DOWN, p );
637     } else {
638         return here.has_flag( flag_GOES_UP, p );
639     }
640 }
641 
can_examine_at(const tripoint & p)642 bool can_examine_at( const tripoint &p )
643 {
644     map &here = get_map();
645     if( here.veh_at( p ) ) {
646         return true;
647     }
648     if( here.has_flag( flag_CONSOLE, p ) ) {
649         return true;
650     }
651     if( here.has_items( p ) ) {
652         return true;
653     }
654     const furn_t &xfurn_t = here.furn( p ).obj();
655     const ter_t &xter_t = here.ter( p ).obj();
656 
657     if( here.has_furn( p ) && xfurn_t.can_examine() ) {
658         return true;
659     } else if( xter_t.can_examine() ) {
660         return true;
661     }
662 
663     Creature *c = g->critter_at( p );
664     if( c != nullptr && !c->is_avatar() ) {
665         return true;
666     }
667 
668     return here.can_see_trap_at( p, get_player_character() );
669 }
670 
can_pickup_at(const tripoint & p)671 static bool can_pickup_at( const tripoint &p )
672 {
673     bool veh_has_items = false;
674     map &here = get_map();
675     const optional_vpart_position vp = here.veh_at( p );
676     if( vp ) {
677         const int cargo_part = vp->vehicle().part_with_feature( vp->part_index(), "CARGO", false );
678         veh_has_items = cargo_part >= 0 && !vp->vehicle().get_items( cargo_part ).empty();
679     }
680     return here.has_items( p ) || veh_has_items;
681 }
682 
can_interact_at(action_id action,const tripoint & p)683 bool can_interact_at( action_id action, const tripoint &p )
684 {
685     map &here = get_map();
686     tripoint player_pos = get_player_character().pos();
687     switch( action ) {
688         case ACTION_OPEN:
689             return here.open_door( p, !here.is_outside( player_pos ), true );
690         case ACTION_CLOSE: {
691             const optional_vpart_position vp = here.veh_at( p );
692             return ( vp &&
693                      vp->vehicle().next_part_to_close( vp->part_index(),
694                              veh_pointer_or_null( here.veh_at( player_pos ) ) != &vp->vehicle() ) >= 0 ) ||
695                    here.close_door( p, !here.is_outside( player_pos ), true );
696         }
697         case ACTION_BUTCHER:
698             return can_butcher_at( p );
699         case ACTION_MOVE_UP:
700             return can_move_vertical_at( p, 1 );
701         case ACTION_MOVE_DOWN:
702             return can_move_vertical_at( p, -1 );
703         case ACTION_EXAMINE:
704             return can_examine_at( p );
705         case ACTION_PICKUP:
706         case ACTION_PICKUP_FEET:
707             return can_pickup_at( p );
708         default:
709             return false;
710     }
711 }
712 
handle_action_menu()713 action_id handle_action_menu()
714 {
715     const input_context ctxt = get_default_mode_input_context();
716     std::string catgname;
717 
718 #define REGISTER_ACTION( name ) entries.emplace_back( name, true, hotkey_for_action( name, /*maximum_modifier_count=*/1 ), \
719         ctxt.get_action_name( action_ident( name ) ) );
720 #define REGISTER_CATEGORY( name )  categories_by_int[last_category] = name; \
721     catgname = name; \
722     catgname += "…"; \
723     entries.emplace_back( last_category, true, -1, catgname ); \
724     last_category++;
725 
726     // Calculate weightings for the various actions to give the player suggestions
727     // Weight >= 200: Special action only available right now
728     std::map<action_id, int> action_weightings;
729 
730     Character &player_character = get_player_character();
731     // Check if we're in a potential combat situation, if so, sort a few actions to the top.
732     if( !player_character.get_hostile_creatures( 60 ).empty() ) {
733         // Only prioritize movement options if we're not driving.
734         if( !player_character.controlling_vehicle ) {
735             action_weightings[ACTION_CYCLE_MOVE] = 400;
736         }
737         // Only prioritize fire weapon options if we're wielding a ranged weapon.
738         if( player_character.weapon.is_gun() || player_character.weapon.has_flag( flag_REACH_ATTACK ) ) {
739             action_weightings[ACTION_FIRE] = 350;
740         }
741     }
742 
743     // If we're already running, make it simple to toggle running to off.
744     if( player_character.is_running() ) {
745         action_weightings[ACTION_TOGGLE_RUN] = 300;
746     }
747     // If we're already crouching, make it simple to toggle crouching to off.
748     if( player_character.is_crouching() ) {
749         action_weightings[ACTION_TOGGLE_CROUCH] = 300;
750     }
751 
752     map &here = get_map();
753     // Check if we're on a vehicle, if so, vehicle controls should be top.
754     if( here.veh_at( player_character.pos() ) ) {
755         // Make it 300 to prioritize it before examining the vehicle.
756         action_weightings[ACTION_CONTROL_VEHICLE] = 300;
757     }
758 
759     // Check if we can perform one of our actions on nearby terrain. If so,
760     // display that action at the top of the list.
761     for( const tripoint &pos : here.points_in_radius( player_character.pos(), 1 ) ) {
762         if( pos != player_character.pos() ) {
763             // Check for actions that work on nearby tiles
764             if( can_interact_at( ACTION_OPEN, pos ) ) {
765                 action_weightings[ACTION_OPEN] = 200;
766             }
767             if( can_interact_at( ACTION_CLOSE, pos ) ) {
768                 action_weightings[ACTION_CLOSE] = 200;
769             }
770             if( can_interact_at( ACTION_EXAMINE, pos ) ) {
771                 action_weightings[ACTION_EXAMINE] = 200;
772             }
773         } else {
774             // Check for actions that work on own tile only
775             if( can_interact_at( ACTION_BUTCHER, pos ) ) {
776                 action_weightings[ACTION_BUTCHER] = 200;
777             }
778             if( can_interact_at( ACTION_MOVE_UP, pos ) ) {
779                 action_weightings[ACTION_MOVE_UP] = 200;
780             }
781             if( can_interact_at( ACTION_MOVE_DOWN, pos ) ) {
782                 action_weightings[ACTION_MOVE_DOWN] = 200;
783             }
784         }
785     }
786 
787     // sort the map by its weightings
788     std::vector<std::pair<action_id, int> > sorted_pairs;
789     std::copy( action_weightings.begin(), action_weightings.end(),
790                std::back_inserter<std::vector<std::pair<action_id, int> > >( sorted_pairs ) );
791     std::reverse( sorted_pairs.begin(), sorted_pairs.end() );
792 
793     // Default category is called "back"
794     std::string category = "back";
795 
796     while( true ) {
797         std::vector<uilist_entry> entries;
798         uilist_entry *entry;
799         std::map<int, std::string> categories_by_int;
800         int last_category = NUM_ACTIONS + 1;
801 
802         if( category == "back" ) {
803             std::vector<std::pair<action_id, int> >::iterator it;
804             for( it = sorted_pairs.begin(); it != sorted_pairs.end(); ++it ) {
805                 if( it->second >= 200 ) {
806                     REGISTER_ACTION( it->first );
807                 }
808             }
809 
810             REGISTER_CATEGORY( _( "Look" ) );
811             REGISTER_CATEGORY( _( "Interact" ) );
812             REGISTER_CATEGORY( _( "Inventory" ) );
813             REGISTER_CATEGORY( _( "Combat" ) );
814             REGISTER_CATEGORY( _( "Craft" ) );
815             REGISTER_CATEGORY( _( "Info" ) );
816             REGISTER_CATEGORY( _( "Misc" ) );
817             if( hotkey_for_action( ACTION_QUICKSAVE, /*maximum_modifier_count=*/1 ).has_value() ) {
818                 REGISTER_ACTION( ACTION_QUICKSAVE );
819             }
820             REGISTER_ACTION( ACTION_SAVE );
821             if( hotkey_for_action( ACTION_QUICKLOAD, /*maximum_modifier_count=*/1 ).has_value() ) {
822                 REGISTER_ACTION( ACTION_QUICKLOAD );
823             }
824             if( hotkey_for_action( ACTION_SUICIDE, /*maximum_modifier_count=*/1 ).has_value() ) {
825                 REGISTER_ACTION( ACTION_SUICIDE );
826             }
827             REGISTER_ACTION( ACTION_HELP );
828             if( ( entry = &entries.back() ) ) {
829                 // help _is_a menu.
830                 entry->txt += "…";
831             }
832             if( hotkey_for_action( ACTION_DEBUG, /*maximum_modifier_count=*/1 ).has_value() ) {
833                 // register with global key
834                 REGISTER_CATEGORY( _( "Debug" ) );
835                 if( ( entry = &entries.back() ) ) {
836                     entry->hotkey = hotkey_for_action( ACTION_DEBUG, /*maximum_modifier_count=*/1 );
837                 }
838             }
839         } else if( category == _( "Look" ) ) {
840             REGISTER_ACTION( ACTION_LOOK );
841             REGISTER_ACTION( ACTION_PEEK );
842             REGISTER_ACTION( ACTION_LIST_ITEMS );
843             REGISTER_ACTION( ACTION_ZONES );
844             REGISTER_ACTION( ACTION_MAP );
845             REGISTER_ACTION( ACTION_SKY );
846         } else if( category == _( "Inventory" ) ) {
847             REGISTER_ACTION( ACTION_INVENTORY );
848             REGISTER_ACTION( ACTION_ADVANCEDINV );
849             REGISTER_ACTION( ACTION_SORT_ARMOR );
850             REGISTER_ACTION( ACTION_DIR_DROP );
851 
852             // Everything below here can be accessed through
853             // the inventory screen, so it's sorted to the
854             // end of the list.
855             REGISTER_ACTION( ACTION_DROP );
856             REGISTER_ACTION( ACTION_COMPARE );
857             REGISTER_ACTION( ACTION_ORGANIZE );
858             REGISTER_ACTION( ACTION_USE );
859             REGISTER_ACTION( ACTION_WEAR );
860             REGISTER_ACTION( ACTION_TAKE_OFF );
861             REGISTER_ACTION( ACTION_EAT );
862             REGISTER_ACTION( ACTION_OPEN_CONSUME );
863             REGISTER_ACTION( ACTION_READ );
864             REGISTER_ACTION( ACTION_WIELD );
865             REGISTER_ACTION( ACTION_UNLOAD );
866         } else if( category == _( "Debug" ) ) {
867             REGISTER_ACTION( ACTION_DEBUG );
868             if( ( entry = &entries.back() ) ) {
869                 // debug _is_a menu.
870                 entry->txt += "…";
871             }
872 #if !defined(TILES)
873             REGISTER_ACTION( ACTION_TOGGLE_FULLSCREEN );
874 #endif
875 #if defined(TILES)
876             REGISTER_ACTION( ACTION_TOGGLE_PIXEL_MINIMAP );
877             REGISTER_ACTION( ACTION_RELOAD_TILESET );
878 #endif // TILES
879             REGISTER_ACTION( ACTION_TOGGLE_PANEL_ADM );
880             REGISTER_ACTION( ACTION_DISPLAY_SCENT );
881             REGISTER_ACTION( ACTION_DISPLAY_SCENT_TYPE );
882             REGISTER_ACTION( ACTION_DISPLAY_TEMPERATURE );
883             REGISTER_ACTION( ACTION_DISPLAY_VEHICLE_AI );
884             REGISTER_ACTION( ACTION_DISPLAY_VISIBILITY );
885             REGISTER_ACTION( ACTION_DISPLAY_LIGHTING );
886             REGISTER_ACTION( ACTION_DISPLAY_TRANSPARENCY );
887             REGISTER_ACTION( ACTION_DISPLAY_REACHABILITY_ZONES );
888             REGISTER_ACTION( ACTION_DISPLAY_RADIATION );
889             REGISTER_ACTION( ACTION_TOGGLE_DEBUG_MODE );
890         } else if( category == _( "Interact" ) ) {
891             REGISTER_ACTION( ACTION_EXAMINE );
892             REGISTER_ACTION( ACTION_SMASH );
893             REGISTER_ACTION( ACTION_MOVE_DOWN );
894             REGISTER_ACTION( ACTION_MOVE_UP );
895             REGISTER_ACTION( ACTION_OPEN );
896             REGISTER_ACTION( ACTION_CLOSE );
897             REGISTER_ACTION( ACTION_CHAT );
898             REGISTER_ACTION( ACTION_PICKUP );
899             REGISTER_ACTION( ACTION_PICKUP_FEET );
900             REGISTER_ACTION( ACTION_GRAB );
901             REGISTER_ACTION( ACTION_HAUL );
902             REGISTER_ACTION( ACTION_BUTCHER );
903             REGISTER_ACTION( ACTION_LOOT );
904         } else if( category == _( "Combat" ) ) {
905             REGISTER_ACTION( ACTION_CYCLE_MOVE );
906             REGISTER_ACTION( ACTION_RESET_MOVE );
907             REGISTER_ACTION( ACTION_TOGGLE_RUN );
908             REGISTER_ACTION( ACTION_TOGGLE_CROUCH );
909             REGISTER_ACTION( ACTION_OPEN_MOVEMENT );
910             REGISTER_ACTION( ACTION_FIRE );
911             REGISTER_ACTION( ACTION_RELOAD_ITEM );
912             REGISTER_ACTION( ACTION_RELOAD_WEAPON );
913             REGISTER_ACTION( ACTION_RELOAD_WIELDED );
914             REGISTER_ACTION( ACTION_CAST_SPELL );
915             REGISTER_ACTION( ACTION_SELECT_FIRE_MODE );
916             REGISTER_ACTION( ACTION_THROW );
917             REGISTER_ACTION( ACTION_FIRE_BURST );
918             REGISTER_ACTION( ACTION_PICK_STYLE );
919             REGISTER_ACTION( ACTION_TOGGLE_AUTO_TRAVEL_MODE );
920             REGISTER_ACTION( ACTION_TOGGLE_SAFEMODE );
921             REGISTER_ACTION( ACTION_TOGGLE_AUTOSAFE );
922             REGISTER_ACTION( ACTION_IGNORE_ENEMY );
923             REGISTER_ACTION( ACTION_TOGGLE_AUTO_FEATURES );
924             REGISTER_ACTION( ACTION_TOGGLE_AUTO_PULP_BUTCHER );
925             REGISTER_ACTION( ACTION_TOGGLE_AUTO_MINING );
926             REGISTER_ACTION( ACTION_TOGGLE_AUTO_FORAGING );
927         } else if( category == _( "Craft" ) ) {
928             REGISTER_ACTION( ACTION_CRAFT );
929             REGISTER_ACTION( ACTION_RECRAFT );
930             REGISTER_ACTION( ACTION_LONGCRAFT );
931             REGISTER_ACTION( ACTION_CONSTRUCT );
932             REGISTER_ACTION( ACTION_DISASSEMBLE );
933         } else if( category == _( "Info" ) ) {
934             REGISTER_ACTION( ACTION_PL_INFO );
935             REGISTER_ACTION( ACTION_MISSIONS );
936             REGISTER_ACTION( ACTION_SCORES );
937             REGISTER_ACTION( ACTION_FACTIONS );
938             REGISTER_ACTION( ACTION_MORALE );
939             REGISTER_ACTION( ACTION_MESSAGES );
940         } else if( category == _( "Misc" ) ) {
941             REGISTER_ACTION( ACTION_WAIT );
942             REGISTER_ACTION( ACTION_SLEEP );
943             REGISTER_ACTION( ACTION_WORKOUT );
944             REGISTER_ACTION( ACTION_BIONICS );
945             REGISTER_ACTION( ACTION_MUTATIONS );
946             REGISTER_ACTION( ACTION_CONTROL_VEHICLE );
947             REGISTER_ACTION( ACTION_ITEMACTION );
948             REGISTER_ACTION( ACTION_TOGGLE_THIEF_MODE );
949 #if defined(TILES)
950             if( use_tiles ) {
951                 REGISTER_ACTION( ACTION_ZOOM_OUT );
952                 REGISTER_ACTION( ACTION_ZOOM_IN );
953             }
954 #endif
955         }
956 
957         if( category != "back" ) {
958             std::string msg = _( "Back" );
959             msg += "…";
960             entries.emplace_back( 2 * NUM_ACTIONS, true,
961                                   hotkey_for_action( ACTION_ACTIONMENU, /*maximum_modifier_count=*/1 ), msg );
962         }
963 
964         std::string title = _( "Actions" );
965         if( category != "back" ) {
966             catgname = category;
967             capitalize_letter( catgname, 0 );
968             title += ": " + catgname;
969         }
970 
971         uilist smenu;
972         smenu.settext( title );
973         smenu.entries = entries;
974         smenu.query();
975         const int selection = smenu.ret;
976 
977         if( selection < 0 || selection == NUM_ACTIONS ) {
978             return ACTION_NULL;
979         } else if( selection == 2 * NUM_ACTIONS ) {
980             if( category != "back" ) {
981                 category = "back";
982             } else {
983                 return ACTION_NULL;
984             }
985         } else if( selection > NUM_ACTIONS ) {
986             category = categories_by_int[selection];
987         } else {
988             return static_cast<action_id>( selection );
989         }
990     }
991 
992 #undef REGISTER_ACTION
993 #undef REGISTER_CATEGORY
994 }
995 
handle_main_menu()996 action_id handle_main_menu()
997 {
998     const input_context ctxt = get_default_mode_input_context();
999     std::vector<uilist_entry> entries;
1000 
1001     const auto REGISTER_ACTION = [&]( action_id name ) {
1002         entries.emplace_back( name, true, hotkey_for_action( name, /*maximum_modifier_count=*/1 ),
1003                               ctxt.get_action_name( action_ident( name ) ) );
1004     };
1005 
1006     REGISTER_ACTION( ACTION_HELP );
1007     REGISTER_ACTION( ACTION_KEYBINDINGS );
1008     REGISTER_ACTION( ACTION_OPTIONS );
1009     REGISTER_ACTION( ACTION_AUTOPICKUP );
1010     REGISTER_ACTION( ACTION_AUTONOTES );
1011     REGISTER_ACTION( ACTION_SAFEMODE );
1012     REGISTER_ACTION( ACTION_COLOR );
1013     REGISTER_ACTION( ACTION_WORLD_MODS );
1014     REGISTER_ACTION( ACTION_ACTIONMENU );
1015     REGISTER_ACTION( ACTION_QUICKSAVE );
1016     REGISTER_ACTION( ACTION_SAVE );
1017     REGISTER_ACTION( ACTION_DEBUG );
1018 
1019     uilist smenu;
1020     smenu.settext( _( "MAIN MENU" ) );
1021     smenu.entries = entries;
1022     smenu.query();
1023     int selection = smenu.ret;
1024 
1025     if( selection < 0 || selection >= NUM_ACTIONS ) {
1026         return ACTION_NULL;
1027     } else {
1028         return static_cast<action_id>( selection );
1029     }
1030 }
1031 
choose_direction(const std::string & message,const bool allow_vertical)1032 cata::optional<tripoint> choose_direction( const std::string &message, const bool allow_vertical )
1033 {
1034     input_context ctxt( "DEFAULTMODE", keyboard_mode::keycode );
1035     ctxt.set_iso( true );
1036     ctxt.register_directions();
1037     ctxt.register_action( "pause" );
1038     ctxt.register_action( "QUIT" );
1039     ctxt.register_action( "HELP_KEYBINDINGS" );
1040     if( allow_vertical ) {
1041         ctxt.register_action( "LEVEL_UP" );
1042         ctxt.register_action( "LEVEL_DOWN" );
1043     }
1044 
1045     static_popup popup;
1046     //~ %s: "Close where?" "Pry where?" etc.
1047     popup.message( _( "%s (Direction button)" ), message ).on_top( true );
1048 
1049     std::string action;
1050     do {
1051         ui_manager::redraw();
1052         action = ctxt.handle_input();
1053         if( const cata::optional<tripoint> vec = ctxt.get_direction( action ) ) {
1054             FacingDirection &facing = get_player_character().facing;
1055             // Make player's sprite face left/right if interacting with something to the left or right
1056             if( vec->x > 0 ) {
1057                 facing = FacingDirection::RIGHT;
1058             } else if( vec->x < 0 ) {
1059                 facing = FacingDirection::LEFT;
1060             }
1061             return vec;
1062         } else if( action == "pause" ) {
1063             return tripoint_zero;
1064         } else if( action == "LEVEL_UP" ) {
1065             return tripoint_above;
1066         } else if( action == "LEVEL_DOWN" ) {
1067             return tripoint_below;
1068         }
1069     } while( action != "QUIT" );
1070 
1071     add_msg( _( "Never mind." ) );
1072     return cata::nullopt;
1073 }
1074 
choose_adjacent(const std::string & message,const bool allow_vertical)1075 cata::optional<tripoint> choose_adjacent( const std::string &message, const bool allow_vertical )
1076 {
1077     const cata::optional<tripoint> dir = choose_direction( message, allow_vertical );
1078     return dir ? *dir + get_player_character().pos() : dir;
1079 }
1080 
choose_adjacent_highlight(const std::string & message,const std::string & failure_message,const action_id action,bool allow_vertical)1081 cata::optional<tripoint> choose_adjacent_highlight( const std::string &message,
1082         const std::string &failure_message, const action_id action, bool allow_vertical )
1083 {
1084     const std::function<bool( const tripoint & )> f = [&action]( const tripoint & p ) {
1085         return can_interact_at( action, p );
1086     };
1087     return choose_adjacent_highlight( message, failure_message, f, allow_vertical );
1088 }
1089 
choose_adjacent_highlight(const std::string & message,const std::string & failure_message,const std::function<bool (const tripoint &)> & allowed,const bool allow_vertical)1090 cata::optional<tripoint> choose_adjacent_highlight( const std::string &message,
1091         const std::string &failure_message, const std::function<bool ( const tripoint & )> &allowed,
1092         const bool allow_vertical )
1093 {
1094     std::vector<tripoint> valid;
1095     avatar &player_character = get_avatar();
1096     map &here = get_map();
1097     if( allowed ) {
1098         for( const tripoint &pos : here.points_in_radius( player_character.pos(), 1 ) ) {
1099             if( allowed( pos ) ) {
1100                 valid.emplace_back( pos );
1101             }
1102         }
1103     }
1104 
1105     const bool auto_select = get_option<bool>( "AUTOSELECT_SINGLE_VALID_TARGET" );
1106     if( valid.empty() && auto_select ) {
1107         add_msg( failure_message );
1108         return cata::nullopt;
1109     } else if( valid.size() == 1 && auto_select ) {
1110         return valid.back();
1111     }
1112 
1113     shared_ptr_fast<game::draw_callback_t> hilite_cb;
1114     if( !valid.empty() ) {
1115         hilite_cb = make_shared_fast<game::draw_callback_t>( [&]() {
1116             for( const tripoint &pos : valid ) {
1117                 here.drawsq( g->w_terrain, player_character, pos,
1118                              true, true, player_character.pos() + player_character.view_offset );
1119             }
1120         } );
1121         g->add_draw_callback( hilite_cb );
1122     }
1123 
1124     return choose_adjacent( message, allow_vertical );
1125 }
1126