From c9700c6960195798bae63f9ef0ed9e0ff6a1e80b Mon Sep 17 00:00:00 2001 From: Mark Langsdorf Date: Thu, 14 Nov 2019 18:39:32 -0600 Subject: [PATCH] map: properly rotate NPCs during update_mapgen Fixes #35430 When a tinymap is rotated during update_mapgen, the NPCs would be moved via spawn_at_precise, which would clip their position to 0-11,0-11, effectively teleporting them to the upper left of the reality bubble. Rework rotate() to include a variable as to whether the rotation is setpos() safe, and use setpos() to relocate NPCs because that doesn't clip the position and teleport the NPCs. --- src/map.h | 6 +++++- src/mapgen.cpp | 32 ++++++++++++++++++++++---------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/map.h b/src/map.h index a8788221e19b6..3faea041fbeae 100644 --- a/src/map.h +++ b/src/map.h @@ -1336,8 +1336,12 @@ class map } // Not protected/private for mapgen_functions.cpp access - void rotate( int turns ); // Rotates the current map 90*turns degrees clockwise + // Rotates the current map 90*turns degrees clockwise // Useful for houses, shops, etc + // @param turns number of 90 clockwise turns to make + // @param setpos_safe if true, being used outside of mapgen and can use setpos to + // set NPC positions. if false, cannot use setpos + void rotate( int turns, bool setpos_safe = false ); // Monster spawning: public: diff --git a/src/mapgen.cpp b/src/mapgen.cpp index e49359e4f7302..18ecd0c75c42f 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -6684,7 +6684,7 @@ computer *map::add_computer( const tripoint &p, const std::string &name, int sec * degrees. * @param turns How many 90-degree turns to rotate the map. */ -void map::rotate( int turns ) +void map::rotate( int turns, const bool setpos_safe ) { //Handle anything outside the 1-3 range gracefully; rotate(0) is a no-op. @@ -6704,6 +6704,8 @@ void map::rotate( int turns ) for( const std::shared_ptr &i : npcs ) { npc &np = *i; const tripoint sq = np.global_square_location(); + const point local_sq = getlocal( sq ).xy(); + real_coords np_rc; np_rc.fromabs( sq.x, sq.y ); // Note: We are rotating the entire overmap square (2x2 of submaps) @@ -6714,7 +6716,6 @@ void map::rotate( int turns ) // OK, this is ugly: we remove the NPC from the whole map // Then we place it back from scratch // It could be rewritten to utilize the fact that rotation shouldn't cross overmaps - auto npc_ptr = overmap_buffer.remove_npc( np.getID() ); int old_x = np_rc.sub_pos.x; int old_y = np_rc.sub_pos.y; @@ -6725,10 +6726,22 @@ void map::rotate( int turns ) old_y += SEEY; } - const auto new_pos = point{ old_x, old_y } .rotate( turns, { SEEX * 2, SEEY * 2 } ); - - np.spawn_at_precise( { abs_sub.xy() }, { new_pos, abs_sub.z } ); - overmap_buffer.insert_npc( npc_ptr ); + const point new_pos = point{ old_x, old_y } .rotate( turns, { SEEX * 2, SEEY * 2 } ); + if( setpos_safe ) { + // setpos can't be used during mapgen, but spawn_at_precise clips position + // to be between 0-11,0-11 and teleports NPCs when used inside of update_mapgen + // calls + const tripoint new_global_sq = sq - local_sq + new_pos; + const tripoint &new_local_pos = g->m.getlocal( new_global_sq ); + np.setpos( tripoint( new_local_pos ) ); + } else { + // OK, this is ugly: we remove the NPC from the whole map + // Then we place it back from scratch + // It could be rewritten to utilize the fact that rotation shouldn't cross overmaps + std::shared_ptr npc_ptr = overmap_buffer.remove_npc( np.getID() ); + np.spawn_at_precise( { abs_sub.xy() }, { new_pos, abs_sub.z } ); + overmap_buffer.insert_npc( npc_ptr ); + } } clear_vehicle_cache( abs_sub.z ); @@ -7702,15 +7715,14 @@ bool update_mapgen_function_json::update_map( const tripoint &omt_pos, const poi tinymap update_tmap; const tripoint sm_pos = omt_to_sm_copy( omt_pos ); update_tmap.load( sm_pos, false ); - const std::string map_id = overmap_buffer.ter( omt_pos ).id().c_str(); mapgendata md( omt_pos, update_tmap, 0.0f, calendar::start_of_cataclysm, miss ); // If the existing map is rotated, we need to rotate it back to the north // orientation before applying our updates. - int rotation = oter_get_rotation( overmap_buffer.ter( omt_pos ) ); + const int rotation = oter_get_rotation( overmap_buffer.ter( omt_pos ) ); if( rotation > 0 ) { - md.m.rotate( rotation ); + md.m.rotate( rotation, true ); } const bool applied = update_map( md, offset, verify ); @@ -7718,7 +7730,7 @@ bool update_mapgen_function_json::update_map( const tripoint &omt_pos, const poi // If we rotated the map before applying updates, we now need to rotate // it back to where we found it. if( rotation ) { - md.m.rotate( 4 - rotation ); + md.m.rotate( 4 - rotation, true ); } if( applied ) {