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