diff --git a/src/editmap.cpp b/src/editmap.cpp index 3a83692c9cbba..b3511e2151981 100644 --- a/src/editmap.cpp +++ b/src/editmap.cpp @@ -485,9 +485,9 @@ void editmap::uber_draw_ter( const catacurses::window &w, map *m ) } if( refresh_mplans ) { monster *mon = dynamic_cast( critter ); - if( mon != nullptr && mon->pos() != mon->move_target() ) { - for( auto &location : line_to( mon->pos(), mon->move_target() ) ) { - hilights["mplan"].points[location] = 1; + if( mon != nullptr && mon->has_dest() ) { + for( auto &location : line_to( mon->get_location(), mon->get_dest() ) ) { + hilights["mplan"].points[m->getlocal( location )] = 1; } } } diff --git a/src/game.cpp b/src/game.cpp index 146074f1c63e8..cd5a6a50c6080 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -10635,7 +10635,7 @@ void game::vertical_move( int movez, bool force, bool peeking ) // TODO: Remove stair teleport bullshit if( rl_dist( u.pos(), old_pos ) <= 1 ) { for( monster *m : monsters_following ) { - m->set_dest( u.pos() ); + m->set_dest( u.get_location() ); } } diff --git a/src/gamemode_defense.cpp b/src/gamemode_defense.cpp index 5557c1ef559fb..9898d65cc2042 100644 --- a/src/gamemode_defense.cpp +++ b/src/gamemode_defense.cpp @@ -1361,7 +1361,7 @@ std::vector defense_game::pick_monster_wave() void defense_game::spawn_wave_monster( const mtype_id &type ) { - tripoint player_pos = get_player_character().pos(); + const tripoint_abs_ms player_pos = get_player_character().get_location(); map &here = get_map(); for( int tries = 0; tries < 1000; tries++ ) { point pnt; diff --git a/src/iuse.cpp b/src/iuse.cpp index 4330f92eb2d32..d0b180c270c59 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -9179,27 +9179,25 @@ cata::optional iuse::lux_meter( Character *p, item *, bool, const tripoint return 0; } -cata::optional iuse::directional_hologram( Character *p, item *it, bool, const tripoint &pos ) +cata::optional iuse::directional_hologram( Character *p, item *it, bool, const tripoint & ) { if( it->is_armor() && !( p->is_worn( *it ) ) ) { p->add_msg_if_player( m_neutral, _( "You need to wear the %1$s before activating it." ), it->tname() ); return cata::nullopt; } - const cata::optional posp_ = choose_adjacent( _( "Choose hologram direction." ) ); - if( !posp_ ) { + const cata::optional posp = choose_adjacent( _( "Choose hologram direction." ) ); + if( !posp ) { return cata::nullopt; } - const tripoint posp = *posp_; + const tripoint delta = *posp - get_player_character().pos(); - monster *const hologram = g->place_critter_at( mon_hologram, posp ); + monster *const hologram = g->place_critter_at( mon_hologram, *posp ); if( !hologram ) { p->add_msg_if_player( m_info, _( "Can't create a hologram there." ) ); return cata::nullopt; } - tripoint target = pos; - target.x = p->posx() + 4 * SEEX * ( posp.x - p->posx() ); - target.y = p->posy() + 4 * SEEY * ( posp.y - p->posy() ); + tripoint_abs_ms target = p->get_location() + delta * ( 4 * SEEX ); hologram->friendly = -1; hologram->add_effect( effect_docile, 1_hours ); hologram->wandf = -30; diff --git a/src/map.cpp b/src/map.cpp index ed414441af730..344d8aa67c1e8 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -7341,7 +7341,8 @@ void map::copy_grid( const tripoint &to, const tripoint &from ) } } -void map::spawn_monsters_submap_group( const tripoint &gp, mongroup &group, bool ignore_sight ) +void map::spawn_monsters_submap_group( const tripoint &gp, mongroup &group, + const tripoint_abs_sm &submap_pos, bool ignore_sight ) { Character &player_character = get_player_character(); const int s_range = std::min( HALF_MAPSIZE_X, @@ -7453,26 +7454,25 @@ void map::spawn_monsters_submap_group( const tripoint &gp, mongroup &group, bool } // Find horde's target submap - // TODO: fix point types - tripoint horde_target( tripoint( -abs_sub.xy(), abs_sub.z ) + group.target.xy().raw() ); - sm_to_ms( horde_target ); + tripoint_abs_sm horde_target = submap_pos + ( group.target - group.pos ); for( auto &tmp : group.monsters ) { for( int tries = 0; tries < 10 && !locations.empty(); tries++ ) { - const tripoint p = random_entry_removed( locations ); - if( !tmp.can_move_to( p ) ) { + const tripoint local_pos = random_entry_removed( locations ); + const tripoint_abs_ms abs_pos = get_map().getglobal( local_pos ); + if( !tmp.can_move_to( local_pos ) ) { continue; // target can not contain the monster } if( group.horde ) { // Give monster a random point near horde's expected destination - const tripoint rand_dest = horde_target + - point( rng( 0, SEEX ), rng( 0, SEEY ) ); - const int turns = rl_dist( p, rand_dest ) + group.interest; + const point_rel_ms pos_in_sm( rng( 0, SEEX ), rng( 0, SEEY ) ); + const tripoint_abs_ms rand_dest = project_to( horde_target ) + pos_in_sm; + const int turns = rl_dist( abs_pos, rand_dest ) + group.interest; tmp.wander_to( rand_dest, turns ); - add_msg_debug( debugmode::DF_MAP, "%s targeting %d,%d,%d", tmp.disp_name(), - tmp.wander_pos.x, tmp.wander_pos.y, tmp.wander_pos.z ); + add_msg_debug( debugmode::DF_MAP, "%s targeting %s", tmp.disp_name(), + tmp.wander_pos.to_string_writable() ); } - monster *const placed = g->place_critter_at( make_shared_fast( tmp ), p ); + monster *const placed = g->place_critter_at( make_shared_fast( tmp ), local_pos ); if( placed ) { placed->on_load(); } @@ -7485,15 +7485,14 @@ void map::spawn_monsters_submap_group( const tripoint &gp, mongroup &group, bool void map::spawn_monsters_submap( const tripoint &gp, bool ignore_sight ) { - // Load unloaded monsters // TODO: fix point types - overmap_buffer.spawn_monster( tripoint_abs_sm( gp + abs_sub.xy() ) ); - + const tripoint_abs_sm submap_pos( gp + abs_sub.xy() ); + // Load unloaded monsters + overmap_buffer.spawn_monster( submap_pos ); // Only spawn new monsters after existing monsters are loaded. - // TODO: fix point types - auto groups = overmap_buffer.groups_at( tripoint_abs_sm( gp + abs_sub.xy() ) ); + auto groups = overmap_buffer.groups_at( submap_pos ); for( auto &mgp : groups ) { - spawn_monsters_submap_group( gp, *mgp, ignore_sight ); + spawn_monsters_submap_group( gp, *mgp, submap_pos, ignore_sight ); } submap *const current_submap = get_submap_at_grid( gp ); diff --git a/src/map.h b/src/map.h index 12be77a30039d..74ffcd014974a 100644 --- a/src/map.h +++ b/src/map.h @@ -1700,7 +1700,8 @@ class map // Helper #1 - spawns monsters on one submap void spawn_monsters_submap( const tripoint &gp, bool ignore_sight ); // Helper #2 - spawns monsters on one submap and from one group on this submap - void spawn_monsters_submap_group( const tripoint &gp, mongroup &group, bool ignore_sight ); + void spawn_monsters_submap_group( const tripoint &gp, mongroup &group, + const tripoint_abs_sm &submap_pos, bool ignore_sight ); protected: void saven( const tripoint &grid ); diff --git a/src/mattack_actors.cpp b/src/mattack_actors.cpp index 310c059f08efc..7ed19d019876f 100644 --- a/src/mattack_actors.cpp +++ b/src/mattack_actors.cpp @@ -66,13 +66,13 @@ std::unique_ptr leap_actor::clone() const bool leap_actor::call( monster &z ) const { - if( !z.can_act() || !z.move_effects( false ) ) { + if( !z.has_dest() || !z.can_act() || !z.move_effects( false ) ) { return false; } std::vector options; - tripoint target = z.move_target(); - float best_float = trigdist ? trig_dist( z.pos(), target ) : square_dist( z.pos(), target ); + const tripoint_abs_ms target_abs = z.get_dest(); + const float best_float = rl_dist( z.get_location(), target_abs ); if( best_float < min_consider_range || best_float > max_consider_range ) { return false; } @@ -84,6 +84,7 @@ bool leap_actor::call( monster &z ) const return false; } map &here = get_map(); + const tripoint target = here.getlocal( target_abs ); std::multimap candidates; for( const tripoint &candidate : here.points_in_radius( z.pos(), max_range ) ) { if( candidate == z.pos() ) { diff --git a/src/monattack.cpp b/src/monattack.cpp index 6c453625dd75d..f3ea88e695373 100644 --- a/src/monattack.cpp +++ b/src/monattack.cpp @@ -1077,7 +1077,7 @@ void mattack::smash_specific( monster *z, Creature *target ) if( z->has_flag( MF_RIDEABLE_MECH ) ) { z->use_mech_power( -5 ); } - z->set_goal( target->pos() ); + z->set_dest( target->get_location() ); smash( z ); } @@ -1499,24 +1499,32 @@ bool mattack::grow_vine( monster *z ) if( monster *const vine = g->place_critter_around( mon_creeper_vine, z->pos(), 1 ) ) { vine->make_ally( *z ); // Store position of parent hub in vine goal point. - vine->set_goal( z->pos() ); + vine->set_dest( z->get_location() ); } } return true; } +// Return true if the creeper hub that spawned the vine is still there +static bool has_vine_parent( monster *z ) +{ + if( !z->has_dest() || !get_map().inbounds( z->get_dest() ) ) { + return false; + } + const monster *parent = get_creature_tracker().creature_at( z->get_dest() ); + return parent != nullptr && parent->type->id == mon_creeper_hub; +} + bool mattack::vine( monster *z ) { - int vine_neighbors = 0; - map &here = get_map(); - bool parent_out_of_range = !here.inbounds( z->move_target() ); - creature_tracker &creatures = get_creature_tracker(); - monster *parent = creatures.creature_at( z->move_target() ); - if( !parent_out_of_range && ( parent == nullptr || parent->type->id != mon_creeper_hub ) ) { + if( !has_vine_parent( z ) ) { // TODO: Should probably die instead. return true; } + int vine_neighbors = 0; + map &here = get_map(); + creature_tracker &creatures = get_creature_tracker(); z->moves -= 100; for( const tripoint &dest : here.points_in_radius( z->pos(), 1 ) ) { Creature *critter = creatures.creature_at( dest ); @@ -1548,7 +1556,7 @@ bool mattack::vine( monster *z ) } } // Calculate distance from nearest hub - int dist_from_hub = rl_dist( z->pos(), z->move_target() ); + int dist_from_hub = rl_dist( z->get_location(), z->get_dest() ); if( dist_from_hub > 20 || vine_neighbors > 5 || one_in( 7 - vine_neighbors ) || !one_in( dist_from_hub ) ) { return true; @@ -1557,7 +1565,7 @@ bool mattack::vine( monster *z ) vine->make_ally( *z ); vine->reset_special( "VINE" ); // Store position of parent hub in vine goal point. - vine->set_goal( z->move_target() ); + vine->set_dest( z->get_dest() ); } return true; @@ -2390,9 +2398,9 @@ bool mattack::callblobs( monster *z ) // if we want to deal with NPCS and friendly monsters as well. // The strategy is to send about 1/3 of the available blobs after the player, // and keep the rest near the brain blob for protection. - const tripoint enemy = get_player_character().pos(); + const tripoint_abs_ms enemy = get_player_character().get_location(); + const std::vector nearby_points = closest_points_first( z->get_location(), 3 ); std::list allies; - std::vector nearby_points = closest_points_first( z->pos(), 3 ); for( monster &candidate : g->all_monsters() ) { if( candidate.type->in_species( species_SLIME ) && candidate.type->id != mon_blob_brain ) { // Just give the allies consistent assignments. @@ -2405,7 +2413,7 @@ bool mattack::callblobs( monster *z ) int guards = 0; for( std::list::iterator ally = allies.begin(); ally != allies.end(); ++ally, ++guards ) { - tripoint post = enemy; + tripoint_abs_ms post = enemy; if( guards < num_guards ) { // Each guard is assigned a spot in the nearby_points vector based on their order. int assigned_spot = ( nearby_points.size() * guards ) / num_guards; @@ -2424,8 +2432,8 @@ bool mattack::callblobs( monster *z ) bool mattack::jackson( monster *z ) { // Jackson draws nearby zombies into the dance. + const std::vector nearby_points = closest_points_first( z->get_location(), 3 ); std::list allies; - std::vector nearby_points = closest_points_first( z->pos(), 3 ); for( monster &candidate : g->all_monsters() ) { if( candidate.type->in_species( species_ZOMBIE ) && candidate.type->id != mon_zombie_jackson ) { // Just give the allies consistent assignments. @@ -2437,7 +2445,7 @@ bool mattack::jackson( monster *z ) int dancers = 0; bool converted = false; for( auto ally = allies.begin(); ally != allies.end(); ++ally, ++dancers ) { - tripoint post = z->pos(); + tripoint_abs_ms post = z->get_location(); if( dancers < num_dancers ) { // Each dancer is assigned a spot in the nearby_points vector based on their order. int assigned_spot = ( nearby_points.size() * dancers ) / num_dancers; @@ -3062,7 +3070,7 @@ bool mattack::nurse_operate( monster *z ) return false; } // Should designate target as the attack_target - z->set_dest( target->pos() ); + z->set_dest( target->get_location() ); // Check if target is already grabbed by something else if( target->has_effect( effect_grabbed ) ) { diff --git a/src/monmove.cpp b/src/monmove.cpp index 503a1d58f5046..b8d14ed5d7c87 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -70,11 +70,6 @@ static const itype_id itype_pressurized_tank( "pressurized_tank" ); static const species_id species_FUNGUS( "FUNGUS" ); static const species_id species_ZOMBIE( "ZOMBIE" ); -bool monster::wander() -{ - return ( goal == pos() && patrol_route_abs_ms.empty() ); -} - bool monster::is_immune_field( const field_type_id &fid ) const { if( fid == fd_fungal_haze ) { @@ -259,25 +254,6 @@ bool monster::can_move_to( const tripoint &p ) const return can_reach_to( p ) && will_move_to( p ); } -void monster::set_dest( const tripoint &p ) -{ - goal = p; -} - -void monster::unset_dest() -{ - goal = pos(); - path.clear(); -} - -// Move towards p for f more turns--generally if we hear a sound there -// "Stupid" movement; "if (wander_pos.x < posx) posx--;" etc. -void monster::wander_to( const tripoint &p, int f ) -{ - wander_pos = p; - wandf = f; -} - float monster::rate_target( Creature &c, float best, bool smart ) const { const FastDistanceApproximation d = rl_dist_fast( pos(), c.pos() ); @@ -401,7 +377,7 @@ void monster::plan() if( docile ) { if( friendly != 0 && target != nullptr ) { - set_dest( target->pos() ); + set_dest( target->get_location() ); } return; @@ -551,8 +527,7 @@ void monster::plan() } if( swarms ) { if( rating < 5 ) { // Too crowded here - wander_pos.x = posx() * rng( 1, 3 ) - mon.posx(); - wander_pos.y = posy() * rng( 1, 3 ) - mon.posy(); + wander_pos = get_location() + point( rng( 1, 3 ), rng( 1, 3 ) ); wandf = 2; target = nullptr; // Swarm to the furthest ally you can see @@ -602,18 +577,20 @@ void monster::plan() anger = 0; remove_effect( effect_dragging ); } else { - set_dest( couch_loc ); + set_dest( here.getglobal( couch_loc ) ); } } } else if( target != nullptr ) { - tripoint dest = target->pos(); + const tripoint_abs_ms dest = target->get_location(); Creature::Attitude att_to_target = attitude_to( *target ); if( att_to_target == Attitude::HOSTILE && !fleeing ) { set_dest( dest ); } else if( fleeing ) { - set_dest( tripoint( posx() * 2 - dest.x, posy() * 2 - dest.y, posz() ) ); + tripoint_abs_ms away = get_location() - dest + get_location(); + away.z() = posz(); + set_dest( away ); } if( angers_hostile_weak && att_to_target != Attitude::FRIENDLY ) { int hp_per = target->hp_percentage(); @@ -621,30 +598,30 @@ void monster::plan() anger += 10 - static_cast( hp_per / 10 ); } } - } else if( !patrol_route_abs_ms.empty() ) { + } else if( !patrol_route.empty() ) { // If we have a patrol route and no target, find the current step on the route - tripoint next_stop_loc_ms = here.getlocal( patrol_route_abs_ms.at( next_patrol_point ) ); + tripoint_abs_ms next_stop = patrol_route.at( next_patrol_point ); // if there is more than one patrol point, advance to the next one if we're almost there // this handles impassable obstancles but patrollers can still get stuck - if( ( patrol_route_abs_ms.size() > 1 ) && rl_dist( next_stop_loc_ms, pos() ) < 2 ) { - next_patrol_point = ( next_patrol_point + 1 ) % patrol_route_abs_ms.size(); - next_stop_loc_ms = here.getlocal( patrol_route_abs_ms.at( next_patrol_point ) ); + if( ( patrol_route.size() > 1 ) && rl_dist( next_stop, get_location() ) < 2 ) { + next_patrol_point = ( next_patrol_point + 1 ) % patrol_route.size(); + next_stop = patrol_route.at( next_patrol_point ); } - set_dest( next_stop_loc_ms ); + set_dest( next_stop ); } else if( friendly > 0 && one_in( 3 ) ) { // Grow restless with no targets friendly--; } else if( friendly != 0 && has_effect( effect_led_by_leash ) ) { // visibility doesn't matter, we're getting pulled by a leash - if( rl_dist( pos(), player_character.pos() ) > 1 ) { - set_dest( player_character.pos() ); + if( rl_dist( get_location(), player_character.get_location() ) > 1 ) { + set_dest( player_character.get_location() ); } else { unset_dest(); } } else if( friendly < 0 && sees( player_character ) && !has_flag( MF_PET_WONT_FOLLOW ) ) { - if( rl_dist( pos(), player_character.pos() ) > 2 ) { - set_dest( player_character.pos() ); + if( rl_dist( get_location(), player_character.get_location() ) > 2 ) { + set_dest( player_character.get_location() ); } else { unset_dest(); } @@ -754,7 +731,7 @@ void monster::move() } } // record position before moving to put the player there if we're dragging - tripoint drag_to = here.getabs( pos() ); + tripoint_abs_ms drag_to = get_location(); const bool pacified = has_effect( effect_pacified ); @@ -848,22 +825,22 @@ void monster::move() } // Set attitude to attitude to our current target monster_attitude current_attitude = attitude( nullptr ); - if( !wander() ) { - if( goal == player_character.pos() ) { + if( !is_wandering() ) { + if( get_dest() == player_character.get_location() ) { current_attitude = attitude( &player_character ); } else { for( const npc &guy : g->all_npcs() ) { - if( goal == guy.pos() ) { + if( get_dest() == guy.get_location() ) { current_attitude = attitude( &guy ); } } } } - if( ( current_attitude == MATT_IGNORE && patrol_route_abs_ms.empty() ) || + if( ( current_attitude == MATT_IGNORE && patrol_route.empty() ) || ( ( current_attitude == MATT_FOLLOW || ( has_flag( MF_KEEP_DISTANCE ) && !( current_attitude == MATT_FLEE ) ) ) - && rl_dist( pos(), goal ) <= type->tracking_distance ) ) { + && rl_dist( get_location(), get_dest() ) <= type->tracking_distance ) ) { moves = 0; stumble(); return; @@ -885,27 +862,28 @@ void monster::move() } // If true, don't try to greedily avoid locally bad paths bool pathed = false; + const tripoint local_dest = here.getlocal( get_dest() ); if( try_to_move ) { - if( !wander() ) { + if( !is_wandering() ) { while( !path.empty() && path.front() == pos() ) { path.erase( path.begin() ); } const auto &pf_settings = get_pathfinding_settings(); - if( pf_settings.max_dist >= rl_dist( pos(), goal ) && - ( path.empty() || rl_dist( pos(), path.front() ) >= 2 || path.back() != goal ) ) { + if( pf_settings.max_dist >= rl_dist( get_location(), get_dest() ) && + ( path.empty() || rl_dist( pos(), path.front() ) >= 2 || path.back() != local_dest ) ) { // We need a new path - path = here.route( pos(), goal, pf_settings, get_path_avoid() ); + path = here.route( pos(), local_dest, pf_settings, get_path_avoid() ); } // Try to respect old paths, even if we can't pathfind at the moment - if( !path.empty() && path.back() == goal ) { + if( !path.empty() && path.back() == local_dest ) { destination = path.front(); moved = true; pathed = true; } else { // Straight line forward, probably because we can't pathfind (well enough) - destination = goal; + destination = local_dest; moved = true; } } @@ -922,8 +900,8 @@ void monster::move() } if( wandf > 0 && !moved && friendly == 0 ) { // No LOS, no scent, so as a fall-back follow sound unset_dest(); - if( wander_pos != pos() ) { - destination = wander_pos; + if( wander_pos != get_location() ) { + destination = here.getlocal( wander_pos ); moved = true; } } @@ -951,7 +929,7 @@ void monster::move() } } - tripoint next_step; + tripoint_abs_ms next_step; const bool can_open_doors = has_flag( MF_CAN_OPEN_DOORS ); const bool staggers = has_flag( MF_STUMBLES ); if( moved ) { @@ -976,7 +954,7 @@ void monster::move() via_ramp = true; candidate.z -= 1; } - tripoint candidate_abs = get_map().getabs( candidate ); + const tripoint_abs_ms candidate_abs = get_map().getglobal( candidate ); if( candidate.z != posz() ) { bool can_z_move = true; @@ -1054,7 +1032,7 @@ void monster::move() continue; } // Don't bash if we're just tracking a noise. - if( !provocative_sound && wander() && destination == wander_pos ) { + if( !provocative_sound && is_wandering() && destination == here.getlocal( wander_pos ) ) { continue; } const int estimate = here.bash_rating( bash_estimate(), candidate ); @@ -1104,9 +1082,8 @@ void monster::move() if( !dragged_foe->has_effect( effect_grabbed ) ) { dragged_foe = nullptr; remove_effect( effect_dragging ); - } else if( here.getlocal( drag_to ) != pos() && - creatures.creature_at( here.getlocal( drag_to ) ) == nullptr ) { - dragged_foe->setpos( here.getlocal( drag_to ) ); + } else if( drag_to != get_location() && creatures.creature_at( drag_to ) == nullptr ) { + dragged_foe->move_to( drag_to ); } } } else { @@ -1115,7 +1092,7 @@ void monster::move() path.clear(); } if( has_effect( effect_led_by_leash ) ) { - if( rl_dist( pos(), player_character.pos() ) > 2 ) { + if( rl_dist( get_location(), player_character.get_location() ) > 2 ) { // Either failed to keep up with the player or moved away remove_effect( effect_led_by_leash ); add_msg( m_info, _( "You lose hold of a leash." ) ); @@ -1149,7 +1126,7 @@ Character *monster::find_dragged_foe() void monster::nursebot_operate( Character *dragged_foe ) { // No dragged foe, nothing to do. - if( dragged_foe == nullptr ) { + if( dragged_foe == nullptr || !has_dest() ) { return; } @@ -1159,26 +1136,27 @@ void monster::nursebot_operate( Character *dragged_foe ) } creature_tracker &creatures = get_creature_tracker(); - if( rl_dist( pos(), goal ) == 1 && - !get_map().has_flag_furn( ter_furn_flag::TFLAG_AUTODOC_COUCH, goal ) && + map &here = get_map(); + if( rl_dist( get_location(), get_dest() ) == 1 && + !here.has_flag_furn( ter_furn_flag::TFLAG_AUTODOC_COUCH, here.getlocal( get_dest() ) ) && !has_effect( effect_operating ) ) { if( dragged_foe->has_effect( effect_grabbed ) && !has_effect( effect_countdown ) && - ( creatures.creature_at( goal ) == nullptr || - creatures.creature_at( goal ) == dragged_foe ) ) { + ( creatures.creature_at( get_dest() ) == nullptr || + creatures.creature_at( get_dest() ) == dragged_foe ) ) { add_msg( m_bad, _( "The %1$s slowly but firmly puts %2$s down onto the autodoc couch." ), name(), dragged_foe->disp_name() ); - dragged_foe->setpos( goal ); + dragged_foe->move_to( get_dest() ); // There's still time to get away add_effect( effect_countdown, 2_turns ); add_msg( m_bad, _( "The %s produces a syringe full of some translucent liquid." ), name() ); - } else if( creatures.creature_at( goal ) != nullptr && has_effect( effect_dragging ) ) { + } else if( creatures.creature_at( get_dest() ) != nullptr && has_effect( effect_dragging ) ) { sounds::sound( pos(), 8, sounds::sound_t::electronic_speech, string_format( _( "a soft robotic voice say, \"Please step away from the autodoc, this patient needs immediate care.\"" ) ) ); // TODO: Make it able to push NPC/player - push_to( goal, 4, 0 ); + push_to( here.getlocal( get_dest() ), 4, 0 ); } } if( get_effect_dur( effect_countdown ) == 1_turns && !has_effect( effect_operating ) ) { @@ -2102,8 +2080,8 @@ bool monster::will_reach( const point &p ) return true; } - if( can_hear() && wandf > 0 && rl_dist( wander_pos.xy(), p ) <= 2 && - rl_dist( point( posx(), posy() ), wander_pos.xy() ) <= wandf ) { + if( can_hear() && wandf > 0 && rl_dist( get_map().getlocal( wander_pos ).xy(), p ) <= 2 && + rl_dist( get_location().xy(), wander_pos.xy() ) <= wandf ) { return true; } diff --git a/src/monster.cpp b/src/monster.cpp index 6af16bef4759e..d0979d0c837ad 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -235,7 +235,7 @@ void monster::on_move( const tripoint_abs_ms &old_pos ) get_name() ); mounted_player->forced_dismount(); } - if( wander() ) { + if( has_dest() && get_location() == get_dest() ) { unset_dest(); } } @@ -1040,43 +1040,60 @@ bool monster::made_of( phase_id p ) const return type->phase == p; } -void monster::set_goal( const tripoint &p ) -{ - goal = p; -} - void monster::set_patrol_route( const std::vector &patrol_pts_rel_ms ) { - map &here = get_map(); - tripoint base_abs_ms( real_coords( here.getabs( pos().xy() ) ).begin_om_pos(), posz() ); + const tripoint_abs_ms base_abs_ms = project_to( global_omt_location() ); for( const point &patrol_pt : patrol_pts_rel_ms ) { - patrol_route_abs_ms.push_back( base_abs_ms + patrol_pt ); + patrol_route.push_back( base_abs_ms + patrol_pt ); } next_patrol_point = 0; } -void monster::shift( const point &sm_shift ) +void monster::shift( const point & ) { - const point ms_shift = sm_to_ms_copy( sm_shift ); - // TODO: migrate these fields to absolute coords and get rid of shift() - goal -= ms_shift; - if( wandf > 0 ) { - wander_pos -= ms_shift; - } + // TODO: migrate this to absolute coords and get rid of shift() + path.clear(); +} + +bool monster::has_dest() const +{ + return goal.has_value(); +} + +tripoint_abs_ms monster::get_dest() const +{ + return goal ? *goal : get_location(); +} + +void monster::set_dest( const tripoint_abs_ms &p ) +{ + goal = p; +} + +void monster::unset_dest() +{ + goal = cata::nullopt; + path.clear(); +} + +bool monster::is_wandering() const +{ + return !has_dest() && patrol_route.empty(); } -tripoint monster::move_target() const +void monster::wander_to( const tripoint_abs_ms &p, int f ) { - return goal; + wander_pos = p; + wandf = f; } Creature *monster::attack_target() { - if( wander() ) { + if( !has_dest() ) { return nullptr; } - Creature *target = get_creature_tracker().creature_at( move_target() ); + Creature *target = get_creature_tracker().creature_at( get_dest() ); if( target == nullptr || target == this || attitude_to( *target ) == Attitude::FRIENDLY || !sees( *target ) ) { return nullptr; @@ -1279,7 +1296,8 @@ monster_attitude monster::attitude( const Character *u ) const return MATT_FOLLOW; } - if( has_flag( MF_KEEP_DISTANCE ) && rl_dist( pos(), goal ) < type->tracking_distance ) { + if( has_flag( MF_KEEP_DISTANCE ) && + rl_dist( get_location(), get_dest() ) < type->tracking_distance ) { return MATT_FLEE; } @@ -2381,7 +2399,6 @@ void monster::die( Creature *nkiller ) drop_items_on_death(); } if( get_killer() != nullptr ) { - // TODO: should actually be class Character Character *ch = get_killer()->as_character(); if( !is_hallucination() && ch != nullptr ) { get_event_bus().send( ch->getID(), type->id ); @@ -3050,10 +3067,11 @@ void monster::hear_sound( const tripoint &source, const int vol, const int dist, max_error = 1; } - point target( source.xy() + point( rng( -max_error, max_error ), rng( -max_error, max_error ) ) ); + tripoint_abs_ms target = get_map().getglobal( source ) + point( rng( -max_error, max_error ), + rng( -max_error, max_error ) ); // target_z will require some special check due to soil muffling sounds - int wander_turns = volume * ( goodhearing ? 6 : 1 ); + const int wander_turns = volume * ( goodhearing ? 6 : 1 ); // again, already following a more interesting sound if( wander_turns < wandf ) { return; @@ -3063,11 +3081,15 @@ void monster::hear_sound( const tripoint &source, const int vol, const int dist, if( morale >= 0 && anger >= 10 ) { // TODO: Add a proper check for fleeing attitude // but cache it nicely, because this part is called a lot - wander_to( tripoint( target, source.z ), wander_turns ); + wander_to( target, wander_turns ); } else if( morale < 0 ) { - // Monsters afraid of sound should not go towards sound - wander_to( -target + tripoint( 2 * posx(), 2 * posy(), 2 * posz() - source.z ), - wander_turns ); + // Monsters afraid of sound should not go towards sound. + // Move towards a point on the opposite side of us from the target. + // TODO: make the destination scale with the sound and handle + // the case when (x,y) is the same by picking a random direction + tripoint_abs_ms away = get_location() + ( get_location() - target ); + away.z() = posz(); + wander_to( away, wander_turns ); } } diff --git a/src/monster.h b/src/monster.h index c82167f9b9a27..84d7fc61f82a7 100644 --- a/src/monster.h +++ b/src/monster.h @@ -180,15 +180,9 @@ class monster : public Creature void deserialize( JsonIn &jsin ); void deserialize( JsonIn &jsin, const tripoint_abs_sm &submap_loc ); - tripoint move_target() const; // Returns point at the end of the monster's current plans - Creature *attack_target(); // Returns the creature at the end of plans (if hostile) - - // Movement - void shift( const point &sm_shift ); // Shifts the monster to the appropriate submap - void set_goal( const tripoint &p ); + // Performs any necessary coordinate updates due to map shift. + void shift( const point &sm_shift ); void set_patrol_route( const std::vector &patrol_pts_rel_ms ); - // Updates current pos AND our plans - bool wander(); // Returns true if we have no plans /** * Checks whether we can move to/through p. This does not account for bashing. @@ -206,11 +200,19 @@ class monster : public Creature bool will_reach( const point &p ); // Do we have plans to get to (x, y)? int turns_to_reach( const point &p ); // How long will it take? - // Go in a straight line to p - void set_dest( const tripoint &p ); + // Returns true if the monster has a current goal + bool has_dest() const; + // Returns point at the end of the monster's current plans + tripoint_abs_ms get_dest() const; + // Returns the creature at the end of plans (if hostile) + Creature *attack_target(); + // Go towards p using the monster's pathfinding settings. + void set_dest( const tripoint_abs_ms &p ); // Reset our plans, we've become aimless. void unset_dest(); + // Returns true if the monster has no plans. + bool is_wandering() const; /** * Set p as wander destination. * @@ -221,8 +223,7 @@ class monster : public Creature * @param f The priority of the destination, as well as how long we should * wander towards there. */ - void wander_to( const tripoint &p, int f ); // Try to get to (x, y), we don't know - // the route. Give up after f steps. + void wander_to( const tripoint_abs_ms &p, int f ); // How good of a target is given creature (checks for visibility) float rate_target( Creature &c, float best, bool smart = false ) const; @@ -466,10 +467,10 @@ class monster : public Creature using Creature::add_msg_debug_player_or_npc; void add_msg_debug_player_or_npc( debugmode::debug_filter type, const std::string &player_msg, const std::string &npc_msg ) const override; - // TEMP VALUES - tripoint wander_pos; // Wander destination - Just try to move in that direction + + tripoint_abs_ms wander_pos; // Wander destination - Just try to move in that direction bool provocative_sound = false; // Are we wandering toward something we think is alive? - int wandf = 0; // Urge to wander - Increased by sound, decrements each move + int wandf = 0; // Urge to is_wandering - Increased by sound, decrements each move std::vector inv; // Inventory Character *mounted_player = nullptr; // player that is mounting this creature character_id mounted_player_id; // id of player that is mounting this creature ( for save/load ) @@ -559,7 +560,7 @@ class monster : public Creature private: int hp = 0; std::map special_attacks; - tripoint goal; + cata::optional goal; bool dead = false; /** Normal upgrades **/ int next_upgrade_time(); @@ -574,7 +575,7 @@ class monster : public Creature /** Found path. Note: Not used by monsters that don't pathfind! **/ std::vector path; /** patrol points for monsters that can pathfind and have a patrol route! **/ - std::vector patrol_route_abs_ms; + std::vector patrol_route; int next_patrol_point = -1; std::bitset effect_cache; diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 66f84df9d0c00..2299a285dfe96 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -2102,7 +2102,7 @@ void inventory::json_load_items( JsonIn &jsin ) //////////////////////////////////////////////////////////////////////////////////////////////////// ///// monster.h -// Remove after 0.G +// TEMPORARY until 0.G void monster::deserialize( JsonIn &jsin, const tripoint_abs_sm &submap_loc ) { JsonObject data = jsin.get_object(); @@ -2117,7 +2117,7 @@ void monster::deserialize( JsonIn &jsin ) load( data ); } -// Remove after 0.G +// TEMPORARY until 0.G void monster::load( const JsonObject &data, const tripoint_abs_sm &submap_loc ) { load( data ); @@ -2125,10 +2125,16 @@ void monster::load( const JsonObject &data, const tripoint_abs_sm &submap_loc ) // When loading an older save in which the monster's absolute location is not serialized // and the monster is not in the current map, the submap location inferred by load() // will be wrong. Use the supplied argument to fix it. + const tripoint_abs_ms old_loc = get_location(); point_abs_sm wrong_submap; tripoint_sm_ms local_pos; std::tie( wrong_submap, local_pos ) = project_remain( get_location() ); set_location( project_combine( submap_loc.xy(), local_pos ) ); + // adjust other relative coordinates that would be subject to the same error + wander_pos = wander_pos - old_loc + get_location(); + if( goal ) { + *goal = *goal - old_loc + get_location(); + } } } @@ -2138,7 +2144,17 @@ void monster::load( const JsonObject &data ) // TEMPORARY until 0.G if( !data.has_member( "location" ) ) { - set_location( tripoint_abs_ms( get_map().getabs( read_legacy_creature_pos( data ) ) ) ); + set_location( get_map().getglobal( read_legacy_creature_pos( data ) ) ); + tripoint wand; + data.read( "wandx", wand.x ); + data.read( "wandy", wand.y ); + data.read( "wandz", wand.z ); + wander_pos = get_map().getglobal( wand ); + tripoint destination; + data.read( "destination", destination ); + if( destination != tripoint_zero ) { + goal = get_location() + destination; + } } std::string sidtmp; @@ -2147,16 +2163,13 @@ void monster::load( const JsonObject &data ) type = &mtype_id( sidtmp ).obj(); data.read( "unique_name", unique_name ); + data.read( "goal", goal ); data.read( "provocative_sound", provocative_sound ); data.read( "wandf", wandf ); - data.read( "wandx", wander_pos.x ); - data.read( "wandy", wander_pos.y ); - if( data.read( "wandz", wander_pos.z ) ) { - wander_pos.z = posz(); - } + data.read( "wander_pos", wander_pos ); if( data.has_int( "next_patrol_point" ) ) { data.read( "next_patrol_point", next_patrol_point ); - data.read( "patrol_route", patrol_route_abs_ms ); + data.read( "patrol_route", patrol_route ); } if( data.has_object( "tied_item" ) ) { JsonIn *tied_item_json = data.get_raw( "tied_item" ); @@ -2251,11 +2264,6 @@ void monster::load( const JsonObject &data ) data.read( "fish_population", fish_population ); data.read( "summon_time_limit", summon_time_limit ); - // This is relative to the monster so it isn't invalidated by map shifting. - tripoint destination; - data.read( "destination", destination ); - goal = pos() + destination; - upgrades = data.get_bool( "upgrades", type->upgrades ); upgrade_time = data.get_int( "upgrade_time", -1 ); @@ -2302,13 +2310,12 @@ void monster::store( JsonOut &json ) const Creature::store( json ); json.member( "typeid", type->id ); json.member( "unique_name", unique_name ); - json.member( "wandx", wander_pos.x ); - json.member( "wandy", wander_pos.y ); - json.member( "wandz", wander_pos.z ); - json.member( "provocative_sound", provocative_sound ); + json.member( "goal", goal ); + json.member( "wander_pos", wander_pos ); json.member( "wandf", wandf ); - if( !patrol_route_abs_ms.empty() ) { - json.member( "patrol_route", patrol_route_abs_ms ); + json.member( "provocative_sound", provocative_sound ); + if( !patrol_route.empty() ) { + json.member( "patrol_route", patrol_route ); json.member( "next_patrol_point", next_patrol_point ); } json.member( "hp", hp ); @@ -2339,8 +2346,6 @@ void monster::store( JsonOut &json ) const if( battery_item ) { json.member( "battery_item", *battery_item ); } - // Store the relative position of the goal so it loads correctly after a map shift. - json.member( "destination", goal - pos() ); json.member( "ammo", ammo ); json.member( "underwater", underwater ); json.member( "upgrades", upgrades ); @@ -2361,7 +2366,6 @@ void monster::store( JsonOut &json ) const json.member( "dragged_foe_id", dragged_foe_id ); // storing the rider json.member( "mounted_player_id", mounted_player_id ); - json.member( "path", path ); } void mon_special_attack::serialize( JsonOut &json ) const diff --git a/tests/monster_attack_test.cpp b/tests/monster_attack_test.cpp index c6d5d0a9fb7f8..dbb31b84d810b 100644 --- a/tests/monster_attack_test.cpp +++ b/tests/monster_attack_test.cpp @@ -61,7 +61,7 @@ static void test_monster_attack( const tripoint &target_offset, bool expect_atta clear_avatar(); you.setpos( target_location ); monster &test_monster = spawn_test_monster( monster_type, attacker_location ); - test_monster.set_goal( target_location ); + test_monster.set_dest( you.get_location() ); reset_caches( a_zlev, t_zlev ); // Trigger basic attack. CAPTURE( attacker_location ); @@ -181,7 +181,7 @@ TEST_CASE( "monster_throwing_sanity_test", "[throwing],[balance]" ) // and you got a monster const std::string monster_type = "mon_feral_human_pipe"; monster &test_monster = spawn_test_monster( monster_type, attacker_location ); - test_monster.set_goal( target_location ); + test_monster.set_dest( you.get_location() ); const mtype_special_attack &attack = test_monster.type->special_attacks.at( "gun" ); reset_caches( attacker_location.z, target_location.z ); statistics damage_dealt; diff --git a/tests/monster_test.cpp b/tests/monster_test.cpp index dd025782aef39..34d5c7d955544 100644 --- a/tests/monster_test.cpp +++ b/tests/monster_test.cpp @@ -39,7 +39,7 @@ static int moves_to_destination( const std::string &monster_type, monster &test_monster = spawn_test_monster( monster_type, start ); // Get it riled up and give it a goal. test_monster.anger = 100; - test_monster.set_dest( end ); + test_monster.set_dest( get_map().getglobal( end ) ); test_monster.set_moves( 0 ); const int monster_speed = test_monster.get_speed(); int moves_spent = 0; @@ -50,7 +50,7 @@ static int moves_to_destination( const std::string &monster_type, const int moves_before = test_monster.moves; test_monster.move(); moves_spent += moves_before - test_monster.moves; - if( test_monster.pos() == test_monster.move_target() ) { + if( test_monster.get_location() == test_monster.get_dest() ) { g->remove_zombie( test_monster ); return moves_spent; } @@ -107,7 +107,7 @@ static int can_catch_player( const std::string &monster_type, const tripoint &di monster &test_monster = spawn_test_monster( monster_type, monster_start ); // Get it riled up and give it a goal. test_monster.anger = 100; - test_monster.set_dest( test_player.pos() ); + test_monster.set_dest( test_player.get_location() ); test_monster.set_moves( 0 ); const int monster_speed = test_monster.get_speed(); const int target_speed = 100; @@ -136,7 +136,7 @@ static int can_catch_player( const std::string &monster_type, const tripoint &di test_player.mod_moves( -move_cost ); } get_map().clear_traps(); - test_monster.set_dest( test_player.pos() ); + test_monster.set_dest( test_player.get_location() ); test_monster.mod_moves( monster_speed ); while( test_monster.moves >= 0 ) { const int moves_before = test_monster.moves; diff --git a/tests/throwing_test.cpp b/tests/throwing_test.cpp index 711ef172d0e61..2c33d3e49bfa9 100644 --- a/tests/throwing_test.cpp +++ b/tests/throwing_test.cpp @@ -257,13 +257,13 @@ static void test_player_kills_monster( ++turns; mon.process_turn(); - mon.set_dest( you.pos() ); + mon.set_dest( you.get_location() ); while( mon.moves > 0 ) { mon.move(); } // zombie made it to player, we're done with this iteration - if( ( last_range = rl_dist( you.pos(), mon.pos() ) ) <= dist_thresh ) { + if( ( last_range = rl_dist( you.get_location(), mon.get_location() ) ) <= dist_thresh ) { break; }