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