1 #include "avatar_action.h"
2 
3 #include <algorithm>
4 #include <climits>
5 #include <cstdlib>
6 #include <functional>
7 #include <map>
8 #include <memory>
9 #include <ostream>
10 #include <set>
11 #include <string>
12 #include <utility>
13 #include <vector>
14 
15 #include "action.h"
16 #include "activity_actor_definitions.h"
17 #include "avatar.h"
18 #include "bodypart.h"
19 #include "cached_options.h"
20 #include "calendar.h"
21 #include "character.h"
22 #include "creature.h"
23 #include "debug.h"
24 #include "enums.h"
25 #include "flag.h"
26 #include "game.h"
27 #include "game_constants.h"
28 #include "game_inventory.h"
29 #include "inventory.h"
30 #include "item.h"
31 #include "item_location.h"
32 #include "itype.h"
33 #include "line.h"
34 #include "map.h"
35 #include "map_iterator.h"
36 #include "mapdata.h"
37 #include "math_defines.h"
38 #include "memory_fast.h"
39 #include "messages.h"
40 #include "monster.h"
41 #include "move_mode.h"
42 #include "mtype.h"
43 #include "npc.h"
44 #include "options.h"
45 #include "output.h"
46 #include "pimpl.h"
47 #include "player_activity.h"
48 #include "projectile.h"
49 #include "ranged.h"
50 #include "ret_val.h"
51 #include "rng.h"
52 #include "translations.h"
53 #include "type_id.h"
54 #include "value_ptr.h"
55 #include "veh_type.h"
56 #include "vehicle.h"
57 #include "vpart_position.h"
58 
59 class gun_mode;
60 class player;
61 
62 static const efftype_id effect_amigara( "amigara" );
63 static const efftype_id effect_glowing( "glowing" );
64 static const efftype_id effect_harnessed( "harnessed" );
65 static const efftype_id effect_incorporeal( "incorporeal" );
66 static const efftype_id effect_onfire( "onfire" );
67 static const efftype_id effect_pet( "pet" );
68 static const efftype_id effect_relax_gas( "relax_gas" );
69 static const efftype_id effect_ridden( "ridden" );
70 static const efftype_id effect_stunned( "stunned" );
71 
72 static const itype_id itype_swim_fins( "swim_fins" );
73 
74 static const skill_id skill_swimming( "swimming" );
75 
76 static const trait_id trait_GRAZER( "GRAZER" );
77 static const trait_id trait_RUMINANT( "RUMINANT" );
78 static const trait_id trait_SHELL2( "SHELL2" );
79 
80 static const std::string flag_RAMP_END( "RAMP_END" );
81 static const std::string flag_SWIMMABLE( "SWIMMABLE" );
82 
83 #define dbg(x) DebugLog((x),D_SDL) << __FILE__ << ":" << __LINE__ << ": "
84 
move(avatar & you,map & m,const tripoint & d)85 bool avatar_action::move( avatar &you, map &m, const tripoint &d )
86 {
87     if( ( !g->check_safe_mode_allowed() ) || you.has_active_mutation( trait_SHELL2 ) ) {
88         if( you.has_active_mutation( trait_SHELL2 ) ) {
89             add_msg( m_warning, _( "You can't move while in your shell.  Deactivate it to go mobile." ) );
90         }
91         return false;
92     }
93     const bool is_riding = you.is_mounted();
94     tripoint dest_loc;
95     if( d.z == 0 && you.has_effect( effect_stunned ) ) {
96         dest_loc.x = rng( you.posx() - 1, you.posx() + 1 );
97         dest_loc.y = rng( you.posy() - 1, you.posy() + 1 );
98         dest_loc.z = you.posz();
99     } else {
100         dest_loc.x = you.posx() + d.x;
101         dest_loc.y = you.posy() + d.y;
102         dest_loc.z = you.posz() + d.z;
103     }
104 
105     if( dest_loc == you.pos() ) {
106         // Well that sure was easy
107         return true;
108     }
109     bool via_ramp = false;
110     if( m.has_flag( TFLAG_RAMP_UP, dest_loc ) ) {
111         dest_loc.z += 1;
112         via_ramp = true;
113     } else if( m.has_flag( TFLAG_RAMP_DOWN, dest_loc ) ) {
114         dest_loc.z -= 1;
115         via_ramp = true;
116     }
117 
118     if( m.has_flag( TFLAG_MINEABLE, dest_loc ) && g->mostseen == 0 &&
119         get_option<bool>( "AUTO_FEATURES" ) && get_option<bool>( "AUTO_MINING" ) &&
120         !m.veh_at( dest_loc ) && !you.is_underwater() && !you.has_effect( effect_stunned ) &&
121         !is_riding && !you.has_effect( effect_incorporeal ) ) {
122         if( you.weapon.has_flag( flag_DIG_TOOL ) ) {
123             if( you.weapon.type->can_use( "JACKHAMMER" ) && you.weapon.ammo_sufficient() ) {
124                 you.invoke_item( &you.weapon, "JACKHAMMER", dest_loc );
125                 // don't move into the tile until done mining
126                 you.defer_move( dest_loc );
127                 return true;
128             } else if( you.weapon.type->can_use( "PICKAXE" ) ) {
129                 you.invoke_item( &you.weapon, "PICKAXE", dest_loc );
130                 // don't move into the tile until done mining
131                 you.defer_move( dest_loc );
132                 return true;
133             }
134         }
135     }
136 
137     // by this point we're either walking, running, crouching, or attacking, so update the activity level to match
138     if( !is_riding ) {
139         you.set_activity_level( you.current_movement_mode()->exertion_level() );
140     }
141 
142     // If the player is *attempting to* move on the X axis, update facing direction of their sprite to match.
143     point new_d( dest_loc.xy() + point( -you.posx(), -you.posy() ) );
144 
145     if( !tile_iso ) {
146         if( new_d.x > 0 ) {
147             you.facing = FacingDirection::RIGHT;
148             if( is_riding ) {
149                 you.mounted_creature->facing = FacingDirection::RIGHT;
150             }
151         } else if( new_d.x < 0 ) {
152             you.facing = FacingDirection::LEFT;
153             if( is_riding ) {
154                 you.mounted_creature->facing = FacingDirection::LEFT;
155             }
156         }
157     } else {
158         //
159         // iso:
160         //
161         // right key            =>  +x -y       FacingDirection::RIGHT
162         // left key             =>  -x +y       FacingDirection::LEFT
163         // up key               =>  +x +y       ______
164         // down key             =>  -x -y       ______
165         // y: left-up key       =>  __ +y       FacingDirection::LEFT
166         // u: right-up key      =>  +x __       FacingDirection::RIGHT
167         // b: left-down key     =>  -x __       FacingDirection::LEFT
168         // n: right-down key    =>  __ -y       FacingDirection::RIGHT
169         //
170         // right key            =>  +x -y       FacingDirection::RIGHT
171         // u: right-up key      =>  +x __       FacingDirection::RIGHT
172         // n: right-down key    =>  __ -y       FacingDirection::RIGHT
173         // up key               =>  +x +y       ______
174         // down key             =>  -x -y       ______
175         // left key             =>  -x +y       FacingDirection::LEFT
176         // y: left-up key       =>  __ +y       FacingDirection::LEFT
177         // b: left-down key     =>  -x __       FacingDirection::LEFT
178         //
179         // right key            =>  +x +y       FacingDirection::RIGHT
180         // u: right-up key      =>  +x __       FacingDirection::RIGHT
181         // n: right-down key    =>  __ +y       FacingDirection::RIGHT
182         // up key               =>  +x -y       ______
183         // left key             =>  -x -y       FacingDirection::LEFT
184         // b: left-down key     =>  -x __       FacingDirection::LEFT
185         // y: left-up key       =>  __ -y       FacingDirection::LEFT
186         // down key             =>  -x +y       ______
187         //
188         if( new_d.x >= 0 && new_d.y >= 0 ) {
189             you.facing = FacingDirection::RIGHT;
190             if( is_riding ) {
191                 auto *mons = you.mounted_creature.get();
192                 mons->facing = FacingDirection::RIGHT;
193             }
194         }
195         if( new_d.y <= 0 && new_d.x <= 0 ) {
196             you.facing = FacingDirection::LEFT;
197             if( is_riding ) {
198                 auto *mons = you.mounted_creature.get();
199                 mons->facing = FacingDirection::LEFT;
200             }
201         }
202     }
203 
204     if( you.has_effect( effect_amigara ) ) {
205         int curdist = INT_MAX;
206         int newdist = INT_MAX;
207         const tripoint minp = tripoint( 0, 0, you.posz() );
208         const tripoint maxp = tripoint( MAPSIZE_X, MAPSIZE_Y, you.posz() );
209         for( const tripoint &pt : m.points_in_rectangle( minp, maxp ) ) {
210             if( m.ter( pt ) == t_fault ) {
211                 int dist = rl_dist( pt, you.pos() );
212                 if( dist < curdist ) {
213                     curdist = dist;
214                 }
215                 dist = rl_dist( pt, dest_loc );
216                 if( dist < newdist ) {
217                     newdist = dist;
218                 }
219             }
220         }
221         if( newdist > curdist ) {
222             add_msg( m_info, _( "You cannot pull yourself away from the faultline…" ) );
223             return false;
224         }
225     }
226 
227     dbg( D_PEDANTIC_INFO ) << "game:plmove: From (" <<
228                            you.posx() << "," << you.posy() << "," << you.posz() << ") to (" <<
229                            dest_loc.x << "," << dest_loc.y << "," << dest_loc.z << ")";
230 
231     if( g->disable_robot( dest_loc ) ) {
232         return false;
233     }
234 
235     // Check if our movement is actually an attack on a monster or npc
236     // Are we displacing a monster?
237 
238     bool attacking = false;
239     if( g->critter_at( dest_loc ) ) {
240         attacking = true;
241     }
242 
243     if( !you.move_effects( attacking ) ) {
244         you.moves -= 100;
245         return false;
246     }
247 
248     if( monster *const mon_ptr = g->critter_at<monster>( dest_loc, true ) ) {
249         monster &critter = *mon_ptr;
250         if( critter.friendly == 0 &&
251             !critter.has_effect( effect_pet ) ) {
252             if( you.is_auto_moving() ) {
253                 add_msg( m_warning, _( "Monster in the way.  Auto-move canceled." ) );
254                 add_msg( m_info, _( "Move into the monster to attack." ) );
255                 you.clear_destination();
256                 return false;
257             }
258             if( you.has_effect( effect_relax_gas ) ) {
259                 if( one_in( 8 ) ) {
260                     add_msg( m_good, _( "Your willpower asserts itself, and so do you!" ) );
261                 } else {
262                     you.moves -= rng( 2, 8 ) * 10;
263                     add_msg( m_bad, _( "You're too pacified to strike anything…" ) );
264                     return false;
265                 }
266             }
267             you.melee_attack( critter, true );
268             if( critter.is_hallucination() ) {
269                 critter.die( &you );
270             }
271             g->draw_hit_mon( dest_loc, critter, critter.is_dead() );
272             return false;
273         } else if( critter.has_flag( MF_IMMOBILE ) || critter.has_effect( effect_harnessed ) ||
274                    critter.has_effect( effect_ridden ) ) {
275             add_msg( m_info, _( "You can't displace your %s." ), critter.name() );
276             return false;
277         }
278         // Successful displacing is handled (much) later
279     }
280     // If not a monster, maybe there's an NPC there
281     if( npc *const np_ = g->critter_at<npc>( dest_loc ) ) {
282         npc &np = *np_;
283         if( you.is_auto_moving() ) {
284             add_msg( _( "NPC in the way, Auto-move canceled." ) );
285             add_msg( m_info, _( "Move into the NPC to interact or attack." ) );
286             you.clear_destination();
287             return false;
288         }
289 
290         if( !np.is_enemy() ) {
291             g->npc_menu( np );
292             return false;
293         }
294 
295         you.melee_attack( np, true );
296         np.make_angry();
297         return false;
298     }
299 
300     // GRAB: pre-action checking.
301     int dpart = -1;
302     const optional_vpart_position vp0 = m.veh_at( you.pos() );
303     vehicle *const veh0 = veh_pointer_or_null( vp0 );
304     const optional_vpart_position vp1 = m.veh_at( dest_loc );
305     vehicle *const veh1 = veh_pointer_or_null( vp1 );
306 
307     bool veh_closed_door = false;
308     bool outside_vehicle = ( veh0 == nullptr || veh0 != veh1 );
309     if( veh1 != nullptr ) {
310         dpart = veh1->next_part_to_open( vp1->part_index(), outside_vehicle );
311         veh_closed_door = dpart >= 0 && !veh1->part( dpart ).open;
312     }
313 
314     if( veh0 != nullptr && std::abs( veh0->velocity ) > 100 ) {
315         if( veh1 == nullptr ) {
316             if( query_yn( _( "Dive from moving vehicle?" ) ) ) {
317                 g->moving_vehicle_dismount( dest_loc );
318             }
319             return false;
320         } else if( veh1 != veh0 ) {
321             add_msg( m_info, _( "There is another vehicle in the way." ) );
322             return false;
323         } else if( !vp1.part_with_feature( "BOARDABLE", true ) ) {
324             add_msg( m_info, _( "That part of the vehicle is currently unsafe." ) );
325             return false;
326         }
327     }
328     bool toSwimmable = m.has_flag( flag_SWIMMABLE, dest_loc );
329     bool toDeepWater = m.has_flag( TFLAG_DEEP_WATER, dest_loc );
330     bool fromSwimmable = m.has_flag( flag_SWIMMABLE, you.pos() );
331     bool fromDeepWater = m.has_flag( TFLAG_DEEP_WATER, you.pos() );
332     bool fromBoat = veh0 != nullptr && veh0->is_in_water( fromDeepWater );
333     bool toBoat = veh1 != nullptr && veh1->is_in_water( toDeepWater );
334     if( is_riding ) {
335         if( !you.check_mount_will_move( dest_loc ) ) {
336             if( you.is_auto_moving() ) {
337                 you.clear_destination();
338             }
339             you.moves -= 20;
340             return false;
341         }
342     }
343     // Dive into water!
344     if( toSwimmable && toDeepWater && !toBoat ) {
345         // Requires confirmation if we were on dry land previously
346         if( is_riding ) {
347             auto *mon = you.mounted_creature.get();
348             if( !mon->swims() || mon->get_size() < you.get_size() + 2 ) {
349                 add_msg( m_warning, _( "The %s cannot swim while it is carrying you!" ), mon->get_name() );
350                 return false;
351             }
352         }
353         if( ( fromSwimmable && fromDeepWater && !fromBoat ) || query_yn( _( "Dive into the water?" ) ) ) {
354             if( ( !fromDeepWater || fromBoat ) && you.swim_speed() < 500 ) {
355                 add_msg( _( "You start swimming." ) );
356                 add_msg( m_info, _( "%s to dive underwater." ),
357                          press_x( ACTION_MOVE_DOWN ) );
358             }
359             avatar_action::swim( get_map(), get_avatar(), dest_loc );
360         }
361 
362         g->on_move_effects();
363         return true;
364     }
365 
366     //Wooden Fence Gate (or equivalently walkable doors):
367     // open it if we are walking
368     // vault over it if we are running
369     std::string door_name = m.obstacle_name( dest_loc );
370     if( m.passable_ter_furn( dest_loc )
371         && you.is_walking()
372         && !veh_closed_door
373         && m.open_door( dest_loc, !m.is_outside( you.pos() ) ) ) {
374         you.moves -= 100;
375         you.add_msg_if_player( _( "You open the %s." ), door_name );
376         // if auto-move is on, continue moving next turn
377         if( you.is_auto_moving() ) {
378             you.defer_move( dest_loc );
379         }
380         return true;
381     }
382     if( g->walk_move( dest_loc, via_ramp ) ) {
383         return true;
384     }
385     if( g->phasing_move( dest_loc ) ) {
386         return true;
387     }
388     if( veh_closed_door ) {
389         if( !veh1->handle_potential_theft( dynamic_cast<player &>( you ) ) ) {
390             return true;
391         } else {
392             door_name = veh1->part( dpart ).name();
393             if( outside_vehicle ) {
394                 veh1->open_all_at( dpart );
395             } else {
396                 veh1->open( dpart );
397             }
398             //~ %1$s - vehicle name, %2$s - part name
399             you.add_msg_if_player( _( "You open the %1$s's %2$s." ), veh1->name, door_name );
400         }
401         you.moves -= 100;
402         // if auto-move is on, continue moving next turn
403         if( you.is_auto_moving() ) {
404             you.defer_move( dest_loc );
405         }
406         return true;
407     }
408 
409     if( m.furn( dest_loc ) != f_safe_c && m.open_door( dest_loc, !m.is_outside( you.pos() ) ) ) {
410         you.moves -= 100;
411         if( veh1 != nullptr ) {
412             //~ %1$s - vehicle name, %2$s - part name
413             you.add_msg_if_player( _( "You open the %1$s's %2$s." ), veh1->name, door_name );
414         } else {
415             you.add_msg_if_player( _( "You open the %s." ), door_name );
416         }
417         // if auto-move is on, continue moving next turn
418         if( you.is_auto_moving() ) {
419             you.defer_move( dest_loc );
420         }
421         return true;
422     }
423 
424     // Invalid move
425     const bool waste_moves = you.is_blind() || you.has_effect( effect_stunned );
426     if( waste_moves || dest_loc.z != you.posz() ) {
427         add_msg( _( "You bump into the %s!" ), m.obstacle_name( dest_loc ) );
428         // Only lose movement if we're blind
429         if( waste_moves ) {
430             you.moves -= 100;
431         }
432     } else if( m.ter( dest_loc ) == t_door_locked || m.ter( dest_loc ) == t_door_locked_peep ||
433                m.ter( dest_loc ) == t_door_locked_alarm || m.ter( dest_loc ) == t_door_locked_interior ) {
434         // Don't drain move points for learning something you could learn just by looking
435         add_msg( _( "That door is locked!" ) );
436     } else if( m.ter( dest_loc ) == t_door_bar_locked ) {
437         add_msg( _( "You rattle the bars but the door is locked!" ) );
438     }
439     return false;
440 }
441 
ramp_move(avatar & you,map & m,const tripoint & dest_loc)442 bool avatar_action::ramp_move( avatar &you, map &m, const tripoint &dest_loc )
443 {
444     if( dest_loc.z != you.posz() ) {
445         // No recursive ramp_moves
446         return false;
447     }
448 
449     // We're moving onto a tile with no support, check if it has a ramp below
450     if( !m.has_floor_or_support( dest_loc ) ) {
451         tripoint below( dest_loc.xy(), dest_loc.z - 1 );
452         if( m.has_flag( TFLAG_RAMP, below ) ) {
453             // But we're moving onto one from above
454             const tripoint dp = dest_loc - you.pos();
455             move( you, m, tripoint( dp.xy(), -1 ) );
456             // No penalty for misaligned stairs here
457             // Also cheaper than climbing up
458             return true;
459         }
460 
461         return false;
462     }
463 
464     if( !m.has_flag( TFLAG_RAMP, you.pos() ) ||
465         m.passable( dest_loc ) ) {
466         return false;
467     }
468 
469     // Try to find an aligned end of the ramp that will make our climb faster
470     // Basically, finish walking on the stairs instead of pulling self up by hand
471     bool aligned_ramps = false;
472     for( const tripoint &pt : m.points_in_radius( you.pos(), 1 ) ) {
473         if( rl_dist( pt, dest_loc ) < 2 && m.has_flag( flag_RAMP_END, pt ) ) {
474             aligned_ramps = true;
475             break;
476         }
477     }
478 
479     const tripoint above_u( you.posx(), you.posy(), you.posz() + 1 );
480     if( m.has_floor_or_support( above_u ) ) {
481         add_msg( m_warning, _( "You can't climb here - there's a ceiling above." ) );
482         return false;
483     }
484 
485     const tripoint dp = dest_loc - you.pos();
486     const tripoint old_pos = you.pos();
487     move( you, m, tripoint( dp.xy(), 1 ) );
488     // We can't just take the result of the above function here
489     if( you.pos() != old_pos ) {
490         you.moves -= 50 + ( aligned_ramps ? 0 : 50 );
491     }
492 
493     return true;
494 }
495 
swim(map & m,avatar & you,const tripoint & p)496 void avatar_action::swim( map &m, avatar &you, const tripoint &p )
497 {
498     if( !m.has_flag( flag_SWIMMABLE, p ) ) {
499         dbg( D_ERROR ) << "game:plswim: Tried to swim in "
500                        << m.tername( p ) << "!";
501         debugmsg( "Tried to swim in %s!", m.tername( p ) );
502         return;
503     }
504     if( you.has_effect( effect_onfire ) ) {
505         add_msg( _( "The water puts out the flames!" ) );
506         you.remove_effect( effect_onfire );
507         if( you.is_mounted() ) {
508             monster *mon = you.mounted_creature.get();
509             if( mon->has_effect( effect_onfire ) ) {
510                 mon->remove_effect( effect_onfire );
511             }
512         }
513     }
514     if( you.has_effect( effect_glowing ) ) {
515         add_msg( _( "The water washes off the glowing goo!" ) );
516         you.remove_effect( effect_glowing );
517     }
518     int movecost = you.swim_speed();
519     you.practice( skill_swimming, you.is_underwater() ? 2 : 1 );
520     if( movecost >= 500 ) {
521         if( !you.is_underwater() &&
522             !( you.shoe_type_count( itype_swim_fins ) == 2 ||
523                ( you.shoe_type_count( itype_swim_fins ) == 1 && one_in( 2 ) ) ) ) {
524             add_msg( m_bad, _( "You sink like a rock!" ) );
525             you.set_underwater( true );
526             ///\EFFECT_STR increases breath-holding capacity while sinking
527             you.oxygen = 30 + 2 * you.str_cur;
528         }
529     }
530     if( you.oxygen <= 5 && you.is_underwater() ) {
531         if( movecost < 500 ) {
532             popup( _( "You need to breathe!  (%s to surface.)" ), press_x( ACTION_MOVE_UP ) );
533         } else {
534             popup( _( "You need to breathe but you can't swim!  Get to dry land, quick!" ) );
535         }
536     }
537     bool diagonal = ( p.x != you.posx() && p.y != you.posy() );
538     if( you.in_vehicle ) {
539         m.unboard_vehicle( you.pos() );
540     }
541     if( you.is_mounted() && m.veh_at( you.pos() ).part_with_feature( VPFLAG_BOARDABLE, true ) ) {
542         add_msg( m_warning, _( "You cannot board a vehicle while mounted." ) );
543         return;
544     }
545     if( const auto vp = m.veh_at( p ).part_with_feature( VPFLAG_BOARDABLE, true ) ) {
546         if( !vp->vehicle().handle_potential_theft( dynamic_cast<player &>( you ) ) ) {
547             return;
548         }
549     }
550     tripoint old_abs_pos = m.getabs( you.pos() );
551     you.setpos( p );
552     g->update_map( you );
553 
554     cata_event_dispatch::avatar_moves( old_abs_pos, you, m );
555 
556     if( m.veh_at( you.pos() ).part_with_feature( VPFLAG_BOARDABLE, true ) ) {
557         m.board_vehicle( you.pos(), &you );
558     }
559     you.moves -= ( movecost > 200 ? 200 : movecost ) * ( trigdist && diagonal ? M_SQRT2 : 1 );
560     you.inv->rust_iron_items();
561 
562     if( !you.is_mounted() ) {
563         you.burn_move_stamina( movecost );
564     }
565 
566     body_part_set drenchFlags{ {
567             body_part_leg_l, body_part_leg_r, body_part_torso, body_part_arm_l,
568             body_part_arm_r, body_part_foot_l, body_part_foot_r, body_part_hand_l, body_part_hand_r
569         }
570     };
571 
572     if( you.is_underwater() ) {
573         drenchFlags.unify_set( { { body_part_head, body_part_eyes, body_part_mouth, body_part_hand_l, body_part_hand_r } } );
574     }
575     you.drench( 100, drenchFlags, true );
576 }
577 
rate_critter(const Creature & c)578 static float rate_critter( const Creature &c )
579 {
580     const npc *np = dynamic_cast<const npc *>( &c );
581     if( np != nullptr ) {
582         return np->weapon_value( np->weapon );
583     }
584 
585     const monster *m = dynamic_cast<const monster *>( &c );
586     return m->type->difficulty;
587 }
588 
autoattack(avatar & you,map & m)589 void avatar_action::autoattack( avatar &you, map &m )
590 {
591     int reach = you.weapon.reach_range( you );
592     std::vector<Creature *> critters = you.get_targetable_creatures( reach, true );
593     critters.erase( std::remove_if( critters.begin(), critters.end(), [&you,
594     reach]( const Creature * c ) {
595         if( reach == 1 && !you.is_adjacent( c, true ) ) {
596             return true;
597         }
598         if( !c->is_npc() ) {
599             return false;
600         }
601         return !dynamic_cast<const npc &>( *c ).is_enemy();
602     } ), critters.end() );
603     if( critters.empty() ) {
604         add_msg( m_info, _( "No hostile creature in reach.  Waiting a turn." ) );
605         if( g->check_safe_mode_allowed() ) {
606             you.pause();
607         }
608         return;
609     }
610 
611     Creature &best = **std::max_element( critters.begin(), critters.end(),
612     []( const Creature * l, const Creature * r ) {
613         return rate_critter( *l ) > rate_critter( *r );
614     } );
615 
616     const tripoint diff = best.pos() - you.pos();
617     if( std::abs( diff.x ) <= 1 && std::abs( diff.y ) <= 1 && diff.z == 0 ) {
618         move( you, m, tripoint( diff.xy(), 0 ) );
619         return;
620     }
621 
622     you.reach_attack( best.pos() );
623 }
624 
625 // TODO: Move data/functions related to targeting out of game class
can_fire_weapon(avatar & you,const map & m,const item & weapon)626 bool avatar_action::can_fire_weapon( avatar &you, const map &m, const item &weapon )
627 {
628     if( !weapon.is_gun() ) {
629         debugmsg( "Expected item to be a gun" );
630         return false;
631     }
632 
633     if( you.has_effect( effect_relax_gas ) ) {
634         if( one_in( 5 ) ) {
635             add_msg( m_good, _( "Your eyes steel, and you raise your weapon!" ) );
636         } else {
637             you.moves -= rng( 2, 5 ) * 10;
638             add_msg( m_bad, _( "You can't fire your weapon, it's too heavy…" ) );
639             return false;
640         }
641     }
642 
643     std::vector<std::string> messages;
644 
645     for( const std::pair<const gun_mode_id, gun_mode> &mode_map : weapon.gun_all_modes() ) {
646         bool check_common = gunmode_checks_common( you, m, messages, mode_map.second );
647         bool check_weapon = gunmode_checks_weapon( you, m, messages, mode_map.second );
648         bool can_use_mode = check_common && check_weapon;
649         if( can_use_mode ) {
650             return true;
651         }
652     }
653 
654     for( const std::string &message : messages ) {
655         add_msg( m_info, message );
656     }
657     return false;
658 }
659 
660 /**
661  * Checks if the turret is valid and if the player meets certain conditions for manually firing it.
662  * @param turret Turret to check.
663  * @return True if all conditions are true, otherwise false.
664  */
can_fire_turret(avatar & you,const map & m,const turret_data & turret)665 static bool can_fire_turret( avatar &you, const map &m, const turret_data &turret )
666 {
667     const item &weapon = *turret.base();
668     if( !weapon.is_gun() ) {
669         debugmsg( "Expected turret base to be a gun." );
670         return false;
671     }
672 
673     switch( turret.query() ) {
674         case turret_data::status::no_ammo:
675             add_msg( m_bad, _( "The %s is out of ammo." ), turret.name() );
676             return false;
677 
678         case turret_data::status::no_power:
679             add_msg( m_bad, _( "The %s is not powered." ), turret.name() );
680             return false;
681 
682         case turret_data::status::ready:
683             break;
684 
685         default:
686             debugmsg( "Unknown turret status" );
687             return false;
688     }
689 
690     if( you.has_effect( effect_relax_gas ) ) {
691         if( one_in( 5 ) ) {
692             add_msg( m_good, _( "Your eyes steel, and you aim your weapon!" ) );
693         } else {
694             you.moves -= rng( 2, 5 ) * 10;
695             add_msg( m_bad, _( "You are too pacified to aim the turret…" ) );
696             return false;
697         }
698     }
699 
700     std::vector<std::string> messages;
701 
702     for( const std::pair<const gun_mode_id, gun_mode> &mode_map : weapon.gun_all_modes() ) {
703         bool can_use_mode = gunmode_checks_common( you, m, messages, mode_map.second );
704         if( can_use_mode ) {
705             return true;
706         }
707     }
708 
709     for( const std::string &message : messages ) {
710         add_msg( m_info, message );
711     }
712     return false;
713 }
714 
fire_wielded_weapon(avatar & you)715 void avatar_action::fire_wielded_weapon( avatar &you )
716 {
717     item &weapon = you.weapon;
718     if( weapon.is_gunmod() ) {
719         add_msg( m_info,
720                  _( "The %s must be attached to a gun, it can not be fired separately." ),
721                  weapon.tname() );
722         return;
723     } else if( !weapon.is_gun() ) {
724         return;
725     } else if( weapon.ammo_data() && !weapon.ammo_types().count( weapon.loaded_ammo().ammo_type() ) ) {
726         add_msg( m_info, _( "The %s can't be fired while loaded with incompatible ammunition %s" ),
727                  weapon.tname(), weapon.ammo_current()->nname( 1 ) );
728         return;
729     }
730 
731     you.assign_activity( player_activity( aim_activity_actor::use_wielded() ), false );
732 }
733 
fire_ranged_mutation(Character & you,const item & fake_gun)734 void avatar_action::fire_ranged_mutation( Character &you, const item &fake_gun )
735 {
736     you.assign_activity( player_activity( aim_activity_actor::use_mutation( fake_gun ) ), false );
737 }
738 
fire_ranged_bionic(avatar & you,const item & fake_gun,const units::energy & cost_per_shot)739 void avatar_action::fire_ranged_bionic( avatar &you, const item &fake_gun,
740                                         const units::energy &cost_per_shot )
741 {
742     you.assign_activity(
743         player_activity( aim_activity_actor::use_bionic( fake_gun, cost_per_shot ) ), false );
744 }
745 
fire_turret_manual(avatar & you,map & m,turret_data & turret)746 void avatar_action::fire_turret_manual( avatar &you, map &m, turret_data &turret )
747 {
748     if( !can_fire_turret( you, m, turret ) ) {
749         return;
750     }
751 
752     g->temp_exit_fullscreen();
753     target_handler::trajectory trajectory = target_handler::mode_turret_manual( you, turret );
754 
755     if( !trajectory.empty() ) {
756         turret.fire( you, trajectory.back() );
757     }
758     g->reenter_fullscreen();
759 }
760 
mend(avatar & you,item_location loc)761 void avatar_action::mend( avatar &you, item_location loc )
762 {
763 
764     if( you.fine_detail_vision_mod() > 4 ) {
765         add_msg( m_bad, _( "It's too dark to work on mending this." ) );
766         return;
767     }
768 
769     if( !loc ) {
770         if( you.is_armed() ) {
771             loc = item_location( you, &you.weapon );
772         } else {
773             add_msg( m_info, _( "You're not wielding anything." ) );
774             return;
775         }
776     }
777 
778     if( you.has_item( *loc ) ) {
779         you.mend_item( item_location( loc ) );
780     }
781 }
782 
eat_here(avatar & you)783 bool avatar_action::eat_here( avatar &you )
784 {
785     map &here = get_map();
786     if( ( you.has_active_mutation( trait_RUMINANT ) || you.has_active_mutation( trait_GRAZER ) ) &&
787         ( here.ter( you.pos() ) == t_underbrush || here.ter( you.pos() ) == t_shrub ) ) {
788         if( you.get_hunger() < 20 ) {
789             add_msg( _( "You're too full to eat the leaves from the %s." ), here.ter( you.pos() )->name() );
790             return true;
791         } else {
792             here.ter_set( you.pos(), t_grass );
793             add_msg( _( "You eat the underbrush." ) );
794             item food( "underbrush", calendar::turn, 1 );
795             you.assign_activity( player_activity( consume_activity_actor( food ) ) );
796             return true;
797         }
798     }
799     if( you.has_active_mutation( trait_GRAZER ) && ( here.ter( you.pos() ) == t_grass ||
800             here.ter( you.pos() ) == t_grass_long || here.ter( you.pos() ) == t_grass_tall ) ) {
801         if( you.get_hunger() < 8 ) {
802             add_msg( _( "You're too full to graze." ) );
803             return true;
804         } else {
805             add_msg( _( "You eat the grass." ) );
806             item food( item( "grass", calendar::turn, 1 ) );
807             you.assign_activity( player_activity( consume_activity_actor( food ) ) );
808             if( here.ter( you.pos() ) == t_grass_tall ) {
809                 here.ter_set( you.pos(), t_grass_long );
810             } else if( here.ter( you.pos() ) == t_grass_long ) {
811                 here.ter_set( you.pos(), t_grass );
812             } else {
813                 here.ter_set( you.pos(), t_dirt );
814             }
815             return true;
816         }
817     }
818     if( you.has_active_mutation( trait_GRAZER ) ) {
819         if( here.ter( you.pos() ) == t_grass_golf ) {
820             add_msg( _( "This grass is too short to graze." ) );
821             return true;
822         } else if( here.ter( you.pos() ) == t_grass_dead ) {
823             add_msg( _( "This grass is dead and too mangled for you to graze." ) );
824             return true;
825         } else if( here.ter( you.pos() ) == t_grass_white ) {
826             add_msg( _( "This grass is tainted with paint and thus inedible." ) );
827             return true;
828         }
829     }
830     return false;
831 }
832 
eat(avatar & you,const item_location & loc)833 void avatar_action::eat( avatar &you, const item_location &loc )
834 {
835     std::string filter;
836     if( !you.activity.str_values.empty() ) {
837         filter = you.activity.str_values.back();
838     }
839     avatar_action::eat( you, loc, you.activity.values, you.activity.targets, filter,
840                         you.activity.id() );
841 }
842 
eat(avatar & you,const item_location & loc,const std::vector<int> & consume_menu_selections,const std::vector<item_location> & consume_menu_selected_items,const std::string & consume_menu_filter,activity_id type)843 void avatar_action::eat( avatar &you, const item_location &loc,
844                          const std::vector<int> &consume_menu_selections,
845                          const std::vector<item_location> &consume_menu_selected_items,
846                          const std::string &consume_menu_filter,
847                          activity_id type )
848 {
849     if( !loc ) {
850         you.cancel_activity();
851         add_msg( _( "Never mind." ) );
852         return;
853     }
854     you.assign_activity( player_activity( consume_activity_actor( loc, consume_menu_selections,
855                                           consume_menu_selected_items, consume_menu_filter, type ) ) );
856 }
857 
plthrow(avatar & you,item_location loc,const cata::optional<tripoint> & blind_throw_from_pos)858 void avatar_action::plthrow( avatar &you, item_location loc,
859                              const cata::optional<tripoint> &blind_throw_from_pos )
860 {
861     if( you.has_active_mutation( trait_SHELL2 ) ) {
862         add_msg( m_info, _( "You can't effectively throw while you're in your shell." ) );
863         return;
864     } else if( you.has_effect( effect_incorporeal ) ) {
865         add_msg( m_info, _( "You lack the substance to affect anything." ) );
866         return;
867     }
868     if( you.is_mounted() ) {
869         monster *mons = get_player_character().mounted_creature.get();
870         if( mons->has_flag( MF_RIDEABLE_MECH ) ) {
871             if( !mons->check_mech_powered() ) {
872                 add_msg( m_bad, _( "Your %s refuses to move as its batteries have been drained." ),
873                          mons->get_name() );
874                 return;
875             }
876         }
877     }
878 
879     if( !loc ) {
880         loc = game_menus::inv::titled_menu( you,  _( "Throw item" ),
881                                             _( "You don't have any items to throw." ) );
882     }
883 
884     if( !loc ) {
885         add_msg( _( "Never mind." ) );
886         return;
887     }
888 
889     const ret_val<bool> ret = you.can_wield( *loc );
890     if( !ret.success() ) {
891         add_msg( m_info, "%s", ret.c_str() );
892         return;
893     }
894 
895     // make a copy and get the original.
896     // the copy is thrown and has its and the originals charges set appropriately
897     // or deleted from inventory if its charges(1) or not stackable.
898     item *orig = loc.get_item();
899     item thrown = *orig;
900     int range = you.throw_range( thrown );
901     if( range < 0 ) {
902         add_msg( m_info, _( "You don't have that item." ) );
903         return;
904     } else if( range == 0 ) {
905         add_msg( m_info, _( "That is too heavy to throw." ) );
906         return;
907     }
908 
909     if( you.is_wielding( *orig ) && orig->has_flag( flag_NO_UNWIELD ) ) {
910         // pos == -1 is the weapon, NO_UNWIELD is used for bio_claws_weapon
911         add_msg( m_info, _( "That's part of your body, you can't throw that!" ) );
912         return;
913     }
914 
915     if( you.has_effect( effect_relax_gas ) ) {
916         if( one_in( 5 ) ) {
917             add_msg( m_good, _( "You concentrate mightily, and your body obeys!" ) );
918         } else {
919             you.moves -= rng( 2, 5 ) * 10;
920             add_msg( m_bad, _( "You can't muster up the effort to throw anything…" ) );
921             return;
922         }
923     }
924     // if you're wearing the item you need to be able to take it off
925     if( you.is_worn( *orig ) ) {
926         ret_val<bool> ret = you.can_takeoff( *orig );
927         if( !ret.success() ) {
928             add_msg( m_info, "%s", ret.c_str() );
929             return;
930         }
931     }
932     // you must wield the item to throw it
933     if( !you.is_wielding( *orig ) ) {
934         if( !you.wield( *orig ) ) {
935             return;
936         }
937     }
938 
939     // Shift our position to our "peeking" position, so that the UI
940     // for picking a throw point lets us target the location we couldn't
941     // otherwise see.
942     const tripoint original_player_position = you.pos();
943     if( blind_throw_from_pos ) {
944         you.setpos( *blind_throw_from_pos );
945     }
946 
947     g->temp_exit_fullscreen();
948 
949     target_handler::trajectory trajectory = target_handler::mode_throw( you, you.weapon,
950                                             blind_throw_from_pos.has_value() );
951 
952     // If we previously shifted our position, put ourselves back now that we've picked our target.
953     if( blind_throw_from_pos ) {
954         you.setpos( original_player_position );
955     }
956 
957     if( trajectory.empty() ) {
958         return;
959     }
960 
961     if( you.weapon.count_by_charges() && you.weapon.charges > 1 ) {
962         you.weapon.mod_charges( -1 );
963         thrown.charges = 1;
964     } else {
965         you.remove_weapon();
966     }
967     you.throw_item( trajectory.back(), thrown, blind_throw_from_pos );
968     g->reenter_fullscreen();
969 }
970 
make_active(item_location loc)971 static void make_active( item_location loc )
972 {
973     map &here = get_map();
974     switch( loc.where() ) {
975         case item_location::type::map:
976             here.make_active( loc );
977             break;
978         case item_location::type::vehicle:
979             here.veh_at( loc.position() )->vehicle().make_active( loc );
980             break;
981         default:
982             break;
983     }
984 }
985 
update_lum(item_location loc,bool add)986 static void update_lum( item_location loc, bool add )
987 {
988     switch( loc.where() ) {
989         case item_location::type::map:
990             get_map().update_lum( loc, add );
991             break;
992         default:
993             break;
994     }
995 }
996 
use_item(avatar & you)997 void avatar_action::use_item( avatar &you )
998 {
999     item_location loc;
1000     avatar_action::use_item( you, loc );
1001 }
1002 
use_item(avatar & you,item_location & loc)1003 void avatar_action::use_item( avatar &you, item_location &loc )
1004 {
1005     // Some items may be used without being picked up first
1006     bool use_in_place = false;
1007 
1008     if( !loc ) {
1009         loc = game_menus::inv::use( you );
1010 
1011         if( !loc ) {
1012             add_msg( _( "Never mind." ) );
1013             return;
1014         }
1015     }
1016     int pre_obtain_moves = you.moves;
1017     if( loc->has_flag( flag_ALLOWS_REMOTE_USE ) ) {
1018         use_in_place = true;
1019         // Activate holster on map only if hands are free.
1020     } else if( you.can_wield( *loc ).success() && loc->is_holster() && !loc.held_by( you ) ) {
1021         use_in_place = true;
1022         // Adjustment because in player::wield_contents this amount is refunded.
1023         you.mod_moves( -loc.obtain_cost( you ) );
1024     } else {
1025         item_location::type loc_where = loc.where_recursive();
1026         if( loc_where != item_location::type::character ) {
1027             you.add_msg_if_player( _( "You pick up the %s." ), loc.get_item()->display_name() );
1028             pre_obtain_moves = -1;
1029 
1030         }
1031         loc = loc.obtain( you, 1 );
1032         if( pre_obtain_moves == -1 ) {
1033             pre_obtain_moves = you.moves;
1034         }
1035         if( !loc ) {
1036             debugmsg( "Failed to obtain target item" );
1037             return;
1038         }
1039     }
1040 
1041     if( use_in_place ) {
1042         update_lum( loc, false );
1043         you.use( loc, pre_obtain_moves );
1044         update_lum( loc, true );
1045 
1046         make_active( loc );
1047     } else {
1048         you.use( loc, pre_obtain_moves );
1049     }
1050 
1051     you.invalidate_crafting_inventory();
1052 }
1053 
1054 // Opens up a menu to Unload a container, gun, or tool
1055 // If it's a gun, some gunmods can also be loaded
unload(avatar & you)1056 void avatar_action::unload( avatar &you )
1057 {
1058     item_location loc = g->inv_map_splice( [&you]( const item & it ) {
1059         return you.rate_action_unload( it ) == hint_rating::good;
1060     }, _( "Unload item" ), 1, _( "You have nothing to unload." ) );
1061 
1062     if( !loc ) {
1063         add_msg( _( "Never mind." ) );
1064         return;
1065     }
1066 
1067     you.unload( loc );
1068 }
1069