Skip to content

Commit

Permalink
map: properly rotate NPCs during update_mapgen
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mlangsdorf committed Nov 15, 2019
1 parent c8b02fe commit c9700c6
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 11 deletions.
6 changes: 5 additions & 1 deletion src/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
32 changes: 22 additions & 10 deletions src/mapgen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -6704,6 +6704,8 @@ void map::rotate( int turns )
for( const std::shared_ptr<npc> &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)
Expand All @@ -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;
Expand All @@ -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> 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 );
Expand Down Expand Up @@ -7702,23 +7715,22 @@ 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 );

// 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 ) {
Expand Down

0 comments on commit c9700c6

Please sign in to comment.