diff --git a/src/game.cpp b/src/game.cpp index 1ca52e5da2dbf..640f2ff93bfda 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -11377,8 +11377,13 @@ point game::place_player( const tripoint &dest_loc, bool quick ) return submap_shift; } -void game::place_player_overmap( const tripoint_abs_omt &om_dest, bool move_player ) +void game::place_player_overmap( const tripoint_abs_ms &ms_dest, bool move_player ) { + if( ms_dest == project_to( u.global_sm_location() - point( HALF_MAPSIZE, + HALF_MAPSIZE ) ) + u.pos() ) { + return; // Already there + } + // if player is teleporting around, they don't bring their horse with them if( u.is_mounted() ) { u.remove_effect( effect_riding ); @@ -11399,11 +11404,12 @@ void game::place_player_overmap( const tripoint_abs_omt &om_dest, bool move_play m.rebuild_vehicle_level_caches(); m.access_cache( m.get_abs_sub().z() ).map_memory_cache_dec.reset(); m.access_cache( m.get_abs_sub().z() ).map_memory_cache_ter.reset(); - // offset because load_map expects the coordinates of the top left corner, but the - // player will be centered in the middle of the map. + // Set this now, if game::place_player fails we'll need it to recover. + const tripoint_abs_sm tele_from = u.global_sm_location(); + // The subtraction is to get the reality bubble NW corner from the center position. const tripoint_abs_sm map_sm_pos = - project_to( om_dest ) - point( HALF_MAPSIZE, HALF_MAPSIZE ); - const tripoint player_pos( u.pos().xy(), map_sm_pos.z() ); + project_to( ms_dest ) - point( HALF_MAPSIZE, HALF_MAPSIZE ); + const tripoint_bub_ms player_pos( u.pos_bub().xy(), map_sm_pos.z() ); load_map( map_sm_pos ); load_npcs(); m.spawn_monsters( true ); // Static monsters @@ -11411,8 +11417,37 @@ void game::place_player_overmap( const tripoint_abs_omt &om_dest, bool move_play // update weather now as it could be different on the new location weather.nextweather = calendar::turn; if( move_player ) { - place_player( player_pos ); + place_player( player_pos.raw() ); + } + const tripoint_abs_sm tele_to = u.global_sm_location(); + if( tele_from != tele_to || !move_player ) { + return; + } // else tele_from == tele_to !!! + // We've failed to teleport for some reason (probably monsters occupying destination squares). + // Let's try to recover gracefully. But also throw a warning, this is bad! + debugmsg( "Failed to place player at destination. If you see this outside of debug teleporting it is a bug." ); + update_map( u, ms_dest.z() != tele_from.z() ); + // This recursive call safely calls map::load_map() again after making sure everything has been unloaded properly. + // Basically, its only purpose it to reset the z-level to the z-level you teleported *from*. Otherwise, it's redundant after update_map + // Again, translate the reality bubble reference to a mapsquare one. + place_player_overmap( project_to( tele_from - point( HALF_MAPSIZE, + HALF_MAPSIZE ) ) + player_pos.raw() ); +} + +void game::place_player_overmap( const tripoint_abs_omt &om_dest, bool move_player ) +{ + // Project the bubble reference to a submap reference. + tripoint offset = u.pos() - point( 5 * SEEX, 5 * SEEY ); + + // And then on to an overmap one. + if( abs( u.global_sm_location().x() ) % 2 == 1 ) { + offset.x += SEEX; + } + if( abs( u.global_sm_location().y() ) % 2 == 1 ) { + offset.y += SEEY; } + + place_player_overmap( project_to( om_dest ) + offset, move_player ); } bool game::phasing_move( const tripoint &dest_loc, const bool via_ramp ) diff --git a/src/game.h b/src/game.h index 56ea6185b4a94..8e8d63defeb6e 100644 --- a/src/game.h +++ b/src/game.h @@ -948,6 +948,7 @@ class game void insert_item( drop_locations &targets ); // Places the player at the specified point; hurts feet, lists items etc. point place_player( const tripoint &dest, bool quick = false ); + void place_player_overmap( const tripoint_abs_ms &ms_dest, bool move_player = true ); void place_player_overmap( const tripoint_abs_omt &om_dest, bool move_player = true ); void perhaps_add_random_npc( bool ignore_spawn_timers_and_rates ); static void display_om_pathfinding_progress( size_t open_set, size_t known_size ); diff --git a/tests/water_movement_test.cpp b/tests/water_movement_test.cpp index 25da7932211b5..a8e04186b14e3 100644 --- a/tests/water_movement_test.cpp +++ b/tests/water_movement_test.cpp @@ -118,7 +118,7 @@ TEST_CASE( "avatar_diving", "[diving]" ) GIVEN( "avatar is underwater at z-2" ) { dummy.set_underwater( true ); - dummy.setpos( test_origin + tripoint( 0, 0, -2 ) ); + dummy.setpos( test_origin ); g->vertical_shift( -2 ); WHEN( "avatar dives down" ) { @@ -261,8 +261,14 @@ struct swim_scenario { static int swimming_steps( avatar &swimmer ) { map &here = get_map(); + // This shouldn't work. + CAPTURE( swimmer.pos() ); + avatar_action::move( swimmer, here, swimmer.pos() + tripoint_west ); + CAPTURE( swimmer.pos() ); const tripoint left = swimmer.pos(); const tripoint right = left + tripoint_east; + CAPTURE( left ); + CAPTURE( right ); int steps = 0; constexpr int STOP_STEPS = 9000; int last_moves = swimmer.get_speed(); @@ -272,10 +278,10 @@ static int swimming_steps( avatar &swimmer ) while( swimmer.get_stamina() > 0 && !swimmer.has_effect( effect_winded ) && steps < STOP_STEPS ) { if( steps % 2 == 0 ) { REQUIRE( swimmer.pos() == left ); - REQUIRE( avatar_action::move( swimmer, here, tripoint_east ) ); + REQUIRE( avatar_action::move( swimmer, here, right ) ); } else { REQUIRE( swimmer.pos() == right ); - REQUIRE( avatar_action::move( swimmer, here, tripoint_west ) ); + REQUIRE( avatar_action::move( swimmer, here, left ) ); } ++steps; REQUIRE( swimmer.get_moves() < last_moves ); @@ -905,6 +911,7 @@ static std::map expected_results = { TEST_CASE( "check_swim_move_cost_and_distance_values", "[swimming][slow]" ) { + clear_avatar(); setup_test_lake(); avatar &dummy = get_avatar();