1 #include "overmapbuffer.h"
2 
3 #include <algorithm>
4 #include <climits>
5 #include <iterator>
6 #include <list>
7 #include <map>
8 #include <string>
9 #include <tuple>
10 
11 #include "basecamp.h"
12 #include "calendar.h"
13 #include "cata_assert.h"
14 #include "cata_utility.h"
15 #include "character.h"
16 #include "character_id.h"
17 #include "color.h"
18 #include "common_types.h"
19 #include "coordinate_conversions.h"
20 #include "coordinates.h"
21 #include "debug.h"
22 #include "filesystem.h"
23 #include "game.h"
24 #include "game_constants.h"
25 #include "line.h"
26 #include "map.h"
27 #include "memory_fast.h"
28 #include "mongroup.h"
29 #include "monster.h"
30 #include "npc.h"
31 #include "optional.h"
32 #include "overmap.h"
33 #include "overmap_connection.h"
34 #include "overmap_types.h"
35 #include "path_info.h"
36 #include "point.h"
37 #include "rng.h"
38 #include "simple_pathfinding.h"
39 #include "string_formatter.h"
40 #include "translations.h"
41 #include "vehicle.h"
42 
43 class map_extra;
44 
45 overmapbuffer overmap_buffer;
46 
overmapbuffer()47 overmapbuffer::overmapbuffer()
48     : last_requested_overmap( nullptr )
49 {
50 }
51 
52 const city_reference city_reference::invalid{ nullptr, tripoint_abs_sm(), -1 };
53 
get_distance_from_bounds() const54 int city_reference::get_distance_from_bounds() const
55 {
56     cata_assert( city != nullptr );
57     return distance - omt_to_sm_copy( city->size );
58 }
59 
get_distance_from_bounds() const60 int camp_reference::get_distance_from_bounds() const
61 {
62     cata_assert( camp != nullptr );
63     return distance - omt_to_sm_copy( 4 );
64 }
65 
terrain_filename(const point_abs_om & p)66 std::string overmapbuffer::terrain_filename( const point_abs_om &p )
67 {
68     return string_format( "%s/o.%d.%d", PATH_INFO::world_base_save_path(), p.x(), p.y() );
69 }
70 
player_filename(const point_abs_om & p)71 std::string overmapbuffer::player_filename( const point_abs_om &p )
72 {
73     return string_format( "%s.seen.%d.%d", PATH_INFO::player_base_save_path(), p.x(), p.y() );
74 }
75 
get(const point_abs_om & p)76 overmap &overmapbuffer::get( const point_abs_om &p )
77 {
78     if( last_requested_overmap != nullptr && last_requested_overmap->pos() == p ) {
79         return *last_requested_overmap;
80     }
81 
82     const auto it = overmaps.find( p );
83     if( it != overmaps.end() ) {
84         return *( last_requested_overmap = it->second.get() );
85     }
86 
87     // That constructor loads an existing overmap or creates a new one.
88     overmap &new_om = *( overmaps[ p ] = std::make_unique<overmap>( p ) );
89     new_om.populate();
90     // Note: fix_mongroups might load other overmaps, so overmaps.back() is not
91     // necessarily the overmap at (x,y)
92     fix_mongroups( new_om );
93     fix_npcs( new_om );
94 
95     last_requested_overmap = &new_om;
96     return new_om;
97 }
98 
create_custom_overmap(const point_abs_om & p,overmap_special_batch & specials)99 void overmapbuffer::create_custom_overmap( const point_abs_om &p, overmap_special_batch &specials )
100 {
101     if( last_requested_overmap != nullptr ) {
102         auto om_iter = overmaps.find( p );
103         if( om_iter != overmaps.end() && om_iter->second.get() == last_requested_overmap ) {
104             last_requested_overmap = nullptr;
105         }
106     }
107     overmap &new_om = *( overmaps[ p ] = std::make_unique<overmap>( p ) );
108     new_om.populate( specials );
109 }
110 
fix_mongroups(overmap & new_overmap)111 void overmapbuffer::fix_mongroups( overmap &new_overmap )
112 {
113     for( auto it = new_overmap.zg.begin(); it != new_overmap.zg.end(); ) {
114         auto &mg = it->second;
115         // spawn related code simply sets population to 0 when they have been
116         // transformed into spawn points on a submap, the group can then be removed
117         if( mg.empty() ) {
118             new_overmap.zg.erase( it++ );
119             continue;
120         }
121         // Inside the bounds of the overmap?
122         if( mg.pos.x() >= 0 && mg.pos.y() >= 0 && mg.pos.x() < OMAPX * 2 &&
123             mg.pos.y() < OMAPY * 2 ) {
124             ++it;
125             continue;
126         }
127         point_abs_sm smabs = project_combine( new_overmap.pos(), mg.pos.xy() );
128         point_abs_om omp;
129         point_om_sm sm_rem;
130         std::tie( omp, sm_rem ) = project_remain<coords::om>( smabs );
131         if( !has( omp ) ) {
132             // Don't generate new overmaps, as this can be called from the
133             // overmap-generating code.
134             ++it;
135             continue;
136         }
137         overmap &om = get( omp );
138         mg.pos = tripoint_om_sm( sm_rem, mg.pos.z() );
139         om.add_mon_group( mg );
140         new_overmap.zg.erase( it++ );
141     }
142 }
143 
fix_npcs(overmap & new_overmap)144 void overmapbuffer::fix_npcs( overmap &new_overmap )
145 {
146     // First step: move all npcs that are located outside of the given overmap
147     // into a separate container. After that loop, new_overmap.npcs is no
148     // accessed anymore!
149     decltype( overmap::npcs ) to_relocate;
150     for( auto it = new_overmap.npcs.begin(); it != new_overmap.npcs.end(); ) {
151         npc &np = **it;
152         const tripoint_abs_omt npc_omt_pos = np.global_omt_location();
153         const point_abs_om npc_om_pos = project_to<coords::om>( npc_omt_pos.xy() );
154         const point_abs_om loc = new_overmap.pos();
155         if( npc_om_pos == loc ) {
156             // Nothing to do
157             ++it;
158             continue;
159         }
160         to_relocate.push_back( *it );
161         it = new_overmap.npcs.erase( it );
162     }
163     // Second step: put them back where they belong. This step involves loading
164     // new overmaps (via `get`), which does in turn call this function for the
165     // newly loaded overmaps. This in turn may move NPCs from the second overmap
166     // back into the first overmap. This messes up the iteration of it. The
167     // iteration is therefore done in a separate step above (which does *not*
168     // involve loading new overmaps).
169     for( auto &ptr : to_relocate ) {
170         npc &np = *ptr;
171         const tripoint_abs_omt npc_omt_pos = np.global_omt_location();
172         const point_abs_om npc_om_pos = project_to<coords::om>( npc_omt_pos.xy() );
173         const point_abs_om loc = new_overmap.pos();
174         if( !has( npc_om_pos ) ) {
175             // This can't really happen without save editing
176             // We have no sane option here, just place the NPC on the edge
177             debugmsg( "NPC %s is out of bounds, on non-generated overmap %s",
178                       np.name, loc.to_string() );
179             point_abs_sm npc_sm = project_to<coords::sm>( npc_om_pos );
180             point_abs_sm min = project_to<coords::sm>( loc );
181             point_abs_sm max =
182                 project_to<coords::sm>( loc + point_south_east ) - point_south_east;
183             npc_sm.x() = clamp( npc_sm.x(), min.x(), max.x() );
184             npc_sm.y() = clamp( npc_sm.y(), min.y(), max.y() );
185             // TODO: fix point types
186             np.spawn_at_sm( tripoint_abs_sm( npc_sm, np.posz() ).raw() );
187             new_overmap.npcs.push_back( ptr );
188             continue;
189         }
190 
191         // Simplest case: just move the pointer
192         get( npc_om_pos ).insert_npc( ptr );
193     }
194 }
195 
save()196 void overmapbuffer::save()
197 {
198     for( auto &omp : overmaps ) {
199         // Note: this may throw io errors from std::ofstream
200         omp.second->save();
201     }
202 }
203 
clear()204 void overmapbuffer::clear()
205 {
206     overmaps.clear();
207     known_non_existing.clear();
208     last_requested_overmap = nullptr;
209 }
210 
get_settings(const tripoint_abs_omt & p)211 const regional_settings &overmapbuffer::get_settings( const tripoint_abs_omt &p )
212 {
213     overmap *om = get_om_global( p ).om;
214     return om->get_settings();
215 }
216 
add_note(const tripoint_abs_omt & p,const std::string & message)217 void overmapbuffer::add_note( const tripoint_abs_omt &p, const std::string &message )
218 {
219     overmap_with_local_coords om_loc = get_om_global( p );
220     om_loc.om->add_note( om_loc.local, message );
221 }
222 
delete_note(const tripoint_abs_omt & p)223 void overmapbuffer::delete_note( const tripoint_abs_omt &p )
224 {
225     if( has_note( p ) ) {
226         overmap_with_local_coords om_loc = get_om_global( p );
227         om_loc.om->delete_note( om_loc.local );
228     }
229 }
230 
mark_note_dangerous(const tripoint_abs_omt & p,int radius,bool is_dangerous)231 void overmapbuffer::mark_note_dangerous( const tripoint_abs_omt &p, int radius, bool is_dangerous )
232 {
233     if( has_note( p ) ) {
234         overmap_with_local_coords om_loc = get_om_global( p );
235         om_loc.om->mark_note_dangerous( om_loc.local, radius, is_dangerous );
236     }
237 }
238 
add_extra(const tripoint_abs_omt & p,const string_id<map_extra> & id)239 void overmapbuffer::add_extra( const tripoint_abs_omt &p, const string_id<map_extra> &id )
240 {
241     overmap_with_local_coords om_loc = get_om_global( p );
242     om_loc.om->add_extra( om_loc.local, id );
243 }
244 
delete_extra(const tripoint_abs_omt & p)245 void overmapbuffer::delete_extra( const tripoint_abs_omt &p )
246 {
247     if( has_extra( p ) ) {
248         overmap_with_local_coords om_loc = get_om_global( p );
249         om_loc.om->delete_extra( om_loc.local );
250     }
251 }
252 
get_existing(const point_abs_om & p)253 overmap *overmapbuffer::get_existing( const point_abs_om &p )
254 {
255     if( last_requested_overmap && last_requested_overmap->pos() == p ) {
256         return last_requested_overmap;
257     }
258     const auto it = overmaps.find( p );
259     if( it != overmaps.end() ) {
260         return last_requested_overmap = it->second.get();
261     }
262     if( known_non_existing.count( p ) > 0 ) {
263         // This overmap does not exist on disk (this has already been
264         // checked in a previous call of this function).
265         return nullptr;
266     }
267     if( file_exist( terrain_filename( p ) ) ) {
268         // File exists, load it normally (the get function
269         // indirectly call overmap::open to do so).
270         return &get( p );
271     }
272     // File does not exist (or not readable which is essentially
273     // the same for our usage). A second call of this function with
274     // the same coordinates will not check the file system, and
275     // return early.
276     // If the overmap had been created in the mean time, the previous
277     // loop would have found and returned it.
278     known_non_existing.insert( p );
279     return nullptr;
280 }
281 
has(const point_abs_om & p)282 bool overmapbuffer::has( const point_abs_om &p )
283 {
284     return get_existing( p ) != nullptr;
285 }
286 
287 overmap_with_local_coords
get_om_global(const point_abs_omt & p)288 overmapbuffer::get_om_global( const point_abs_omt &p )
289 {
290     return get_om_global( tripoint_abs_omt( p, 0 ) );
291 }
292 
293 overmap_with_local_coords
get_om_global(const tripoint_abs_omt & p)294 overmapbuffer::get_om_global( const tripoint_abs_omt &p )
295 {
296     point_abs_om om_pos;
297     point_om_omt local;
298     std::tie( om_pos, local ) = project_remain<coords::om>( p.xy() );
299     overmap *om = &get( om_pos );
300     return { om, tripoint_om_omt( local, p.z() ) };
301 }
302 
303 overmap_with_local_coords
get_existing_om_global(const point_abs_omt & p)304 overmapbuffer::get_existing_om_global( const point_abs_omt &p )
305 {
306     return get_existing_om_global( tripoint_abs_omt( p, 0 ) );
307 }
308 
309 overmap_with_local_coords
get_existing_om_global(const tripoint_abs_omt & p)310 overmapbuffer::get_existing_om_global( const tripoint_abs_omt &p )
311 {
312     point_abs_om om_pos;
313     point_om_omt local;
314     std::tie( om_pos, local ) = project_remain<coords::om>( p.xy() );
315     overmap *om = get_existing( om_pos );
316     if( om == nullptr ) {
317         return overmap_with_local_coords{ nullptr, tripoint_om_omt() };
318     }
319 
320     return overmap_with_local_coords{ om, tripoint_om_omt( local, p.z() ) };
321 }
322 
is_omt_generated(const tripoint_abs_omt & loc)323 bool overmapbuffer::is_omt_generated( const tripoint_abs_omt &loc )
324 {
325     if( overmap_with_local_coords om_loc = get_existing_om_global( loc ) ) {
326         return om_loc.om->is_omt_generated( om_loc.local );
327     }
328 
329     // If the overmap doesn't exist, then for sure the local mapgen
330     // hasn't happened.
331     return false;
332 }
333 
has_note(const tripoint_abs_omt & p)334 bool overmapbuffer::has_note( const tripoint_abs_omt &p )
335 {
336     if( const overmap_with_local_coords om_loc = get_existing_om_global( p ) ) {
337         return om_loc.om->has_note( om_loc.local );
338     }
339     return false;
340 }
341 
is_marked_dangerous(const tripoint_abs_omt & p)342 bool overmapbuffer::is_marked_dangerous( const tripoint_abs_omt &p )
343 {
344     if( const overmap_with_local_coords om_loc = get_existing_om_global( p ) ) {
345         return om_loc.om->is_marked_dangerous( om_loc.local );
346     }
347     return false;
348 }
349 
note(const tripoint_abs_omt & p)350 const std::string &overmapbuffer::note( const tripoint_abs_omt &p )
351 {
352     if( const overmap_with_local_coords om_loc = get_existing_om_global( p ) ) {
353         return om_loc.om->note( om_loc.local );
354     }
355     static const std::string empty_string;
356     return empty_string;
357 }
358 
has_extra(const tripoint_abs_omt & p)359 bool overmapbuffer::has_extra( const tripoint_abs_omt &p )
360 {
361     if( const overmap_with_local_coords om_loc = get_existing_om_global( p ) ) {
362         return om_loc.om->has_extra( om_loc.local );
363     }
364     return false;
365 }
366 
extra(const tripoint_abs_omt & p)367 const string_id<map_extra> &overmapbuffer::extra( const tripoint_abs_omt &p )
368 {
369     if( const overmap_with_local_coords om_loc = get_existing_om_global( p ) ) {
370         return om_loc.om->extra( om_loc.local );
371     }
372     static const string_id<map_extra> id;
373     return id;
374 }
375 
is_explored(const tripoint_abs_omt & p)376 bool overmapbuffer::is_explored( const tripoint_abs_omt &p )
377 {
378     if( const overmap_with_local_coords om_loc = get_existing_om_global( p ) ) {
379         return om_loc.om->is_explored( om_loc.local );
380     }
381     return false;
382 }
383 
toggle_explored(const tripoint_abs_omt & p)384 void overmapbuffer::toggle_explored( const tripoint_abs_omt &p )
385 {
386     const overmap_with_local_coords om_loc = get_om_global( p );
387     om_loc.om->explored( om_loc.local ) = !om_loc.om->explored( om_loc.local );
388 }
389 
has_horde(const tripoint_abs_omt & p)390 bool overmapbuffer::has_horde( const tripoint_abs_omt &p )
391 {
392     for( const auto &m : overmap_buffer.monsters_at( p ) ) {
393         if( m->horde ) {
394             return true;
395         }
396     }
397 
398     return false;
399 }
400 
get_horde_size(const tripoint_abs_omt & p)401 int overmapbuffer::get_horde_size( const tripoint_abs_omt &p )
402 {
403     int horde_size = 0;
404     for( const auto &m : overmap_buffer.monsters_at( p ) ) {
405         if( m->horde ) {
406             if( !m->monsters.empty() ) {
407                 horde_size += m->monsters.size();
408             } else {
409                 // We don't know how large this will actually be, because
410                 // population "1" can still result in a zombie pack.
411                 // So we double the population as an estimate to make
412                 // hordes more likely to be visible on the overmap.
413                 horde_size += m->population * 2;
414             }
415         }
416     }
417 
418     return horde_size;
419 }
420 
has_camp(const tripoint_abs_omt & p)421 bool overmapbuffer::has_camp( const tripoint_abs_omt &p )
422 {
423     if( p.z() ) {
424         return false;
425     }
426 
427     const overmap_with_local_coords om_loc = get_existing_om_global( p );
428     if( !om_loc ) {
429         return false;
430     }
431 
432     for( const auto &v : om_loc.om->camps ) {
433         if( v.camp_omt_pos().xy() == p.xy() ) {
434             return true;
435         }
436     }
437 
438     return false;
439 }
440 
has_vehicle(const tripoint_abs_omt & p)441 bool overmapbuffer::has_vehicle( const tripoint_abs_omt &p )
442 {
443     if( p.z() ) {
444         return false;
445     }
446 
447     const overmap_with_local_coords om_loc = get_existing_om_global( p );
448     if( !om_loc ) {
449         return false;
450     }
451 
452     for( const auto &v : om_loc.om->vehicles ) {
453         if( v.second.p == om_loc.local.xy() ) {
454             return true;
455         }
456     }
457 
458     return false;
459 }
460 
get_vehicle(const tripoint_abs_omt & p)461 std::vector<om_vehicle> overmapbuffer::get_vehicle( const tripoint_abs_omt &p )
462 {
463     std::vector<om_vehicle> result;
464     if( p.z() != 0 ) {
465         return result;
466     }
467     const overmap_with_local_coords om_loc = get_existing_om_global( p );
468     if( !om_loc ) {
469         return result;
470     }
471     for( const auto &ov : om_loc.om->vehicles ) {
472         if( ov.second.p == om_loc.local.xy() ) {
473             result.push_back( ov.second );
474         }
475     }
476     return result;
477 }
478 
signal_hordes(const tripoint_abs_sm & center,const int sig_power)479 void overmapbuffer::signal_hordes( const tripoint_abs_sm &center, const int sig_power )
480 {
481     const int radius = sig_power;
482     for( auto &om : get_overmaps_near( center, radius ) ) {
483         const point_abs_sm abs_pos_om = project_to<coords::sm>( om->pos() );
484         const tripoint_rel_sm rel_pos = center - abs_pos_om;
485         // overmap::signal_hordes expects a coordinate relative to the overmap, this is easier
486         // for processing as the monster group stores is location as relative coordinates, too.
487         om->signal_hordes( rel_pos, sig_power );
488     }
489 }
490 
process_mongroups()491 void overmapbuffer::process_mongroups()
492 {
493     // arbitrary radius to include nearby overmaps (aside from the current one)
494     const int radius = MAPSIZE * 2;
495     // TODO: fix point types
496     const tripoint_abs_sm center( get_player_character().global_sm_location() );
497     for( auto &om : get_overmaps_near( center, radius ) ) {
498         om->process_mongroups();
499     }
500 }
501 
move_hordes()502 void overmapbuffer::move_hordes()
503 {
504     // arbitrary radius to include nearby overmaps (aside from the current one)
505     const int radius = MAPSIZE * 2;
506     // TODO: fix point types
507     const tripoint_abs_sm center( get_player_character().global_sm_location() );
508     for( auto &om : get_overmaps_near( center, radius ) ) {
509         om->move_hordes();
510     }
511 }
512 
monsters_at(const tripoint_abs_omt & p)513 std::vector<mongroup *> overmapbuffer::monsters_at( const tripoint_abs_omt &p )
514 {
515     // (x,y) are overmap terrain coordinates, they spawn 2x2 submaps,
516     // but monster groups are defined with submap coordinates.
517     tripoint_abs_sm p_sm = project_to<coords::sm>( p );
518     std::vector<mongroup *> result;
519     for( const point &offset : std::array<point, 4> { { { point_zero }, { point_south }, { point_east }, { point_south_east } } } ) {
520         std::vector<mongroup *> tmp = groups_at( p_sm + offset );
521         result.insert( result.end(), tmp.begin(), tmp.end() );
522     }
523     return result;
524 }
525 
groups_at(const tripoint_abs_sm & p)526 std::vector<mongroup *> overmapbuffer::groups_at( const tripoint_abs_sm &p )
527 {
528     std::vector<mongroup *> result;
529     point_om_sm sm_within_om;
530     point_abs_om omp;
531     std::tie( omp, sm_within_om ) = project_remain<coords::om>( p.xy() );
532     if( !has( omp ) ) {
533         return result;
534     }
535     overmap &om = get( omp );
536     auto groups_range = om.zg.equal_range( tripoint_om_sm( sm_within_om, p.z() ) );
537     for( auto it = groups_range.first; it != groups_range.second; ++it ) {
538         mongroup &mg = it->second;
539         if( mg.empty() ) {
540             continue;
541         }
542         result.push_back( &mg );
543     }
544     return result;
545 }
546 
547 #pragma GCC diagnostic push
548 #pragma GCC diagnostic ignored "-Wmissing-noreturn"
scents_near(const tripoint_abs_omt & origin)549 std::array<std::array<scent_trace, 3>, 3> overmapbuffer::scents_near( const tripoint_abs_omt
550         &origin )
551 {
552     std::array<std::array<scent_trace, 3>, 3> found_traces;
553 
554     for( int x = -1; x <= 1 ; ++x ) {
555         for( int y = -1; y <= 1; ++y ) {
556             tripoint_abs_omt iter = origin + point( x, y );
557             found_traces[x + 1][y + 1] = scent_at( iter );
558         }
559     }
560 
561     return found_traces;
562 }
563 #pragma GCC diagnostic pop
564 
scent_at(const tripoint_abs_omt & p)565 scent_trace overmapbuffer::scent_at( const tripoint_abs_omt &p )
566 {
567     if( const overmap_with_local_coords om_loc = get_existing_om_global( p ) ) {
568         return om_loc.om->scent_at( p );
569     }
570     return scent_trace();
571 }
572 
set_scent(const tripoint_abs_omt & loc,int strength)573 void overmapbuffer::set_scent( const tripoint_abs_omt &loc, int strength )
574 {
575     const overmap_with_local_coords om_loc = get_om_global( loc );
576     scent_trace new_scent( calendar::turn, strength );
577     om_loc.om->set_scent( loc, new_scent );
578 }
579 
move_vehicle(vehicle * veh,const point_abs_ms & old_msp)580 void overmapbuffer::move_vehicle( vehicle *veh, const point_abs_ms &old_msp )
581 {
582     // TODO: fix point types
583     const point_abs_ms new_msp( get_map().getabs( veh->global_pos3().xy() ) );
584     const point_abs_omt old_omt = project_to<coords::omt>( old_msp );
585     const point_abs_omt new_omt = project_to<coords::omt>( new_msp );
586     const overmap_with_local_coords old_om_loc = get_om_global( old_omt );
587     const overmap_with_local_coords new_om_loc = get_om_global( new_omt );
588     if( old_om_loc.om == new_om_loc.om ) {
589         new_om_loc.om->vehicles[veh->om_id].p = new_om_loc.local.xy();
590     } else {
591         old_om_loc.om->vehicles.erase( veh->om_id );
592         add_vehicle( veh );
593     }
594 }
595 
remove_camp(const basecamp & camp)596 void overmapbuffer::remove_camp( const basecamp &camp )
597 {
598     const point_abs_omt omt = camp.camp_omt_pos().xy();
599     const overmap_with_local_coords om_loc = get_om_global( omt );
600     std::vector<basecamp> &camps = om_loc.om->camps;
601     for( auto it = camps.begin(); it != camps.end(); ++it ) {
602         if( it->camp_omt_pos().xy() == omt ) {
603             camps.erase( it );
604             return;
605         }
606     }
607 }
608 
remove_vehicle(const vehicle * veh)609 void overmapbuffer::remove_vehicle( const vehicle *veh )
610 {
611     // TODO: fix point types
612     const point_abs_omt omt( ms_to_omt_copy( get_map().getabs( veh->global_pos3().xy() ) ) );
613     const overmap_with_local_coords om_loc = get_om_global( omt );
614     om_loc.om->vehicles.erase( veh->om_id );
615 }
616 
add_vehicle(vehicle * veh)617 void overmapbuffer::add_vehicle( vehicle *veh )
618 {
619     const point abs_pos = get_map().getabs( veh->global_pos3().xy() );
620     // TODO: fix point types
621     const point_abs_omt omt( ms_to_omt_copy( abs_pos ) );
622     const overmap_with_local_coords om_loc = get_om_global( omt );
623     int id = om_loc.om->vehicles.size() + 1;
624     // this *should* be unique but just in case
625     while( om_loc.om->vehicles.count( id ) > 0 ) {
626         id++;
627     }
628     om_vehicle &tracked_veh = om_loc.om->vehicles[id];
629     tracked_veh.p = om_loc.local.xy();
630     tracked_veh.name = veh->name;
631     veh->om_id = id;
632 }
633 
add_camp(const basecamp & camp)634 void overmapbuffer::add_camp( const basecamp &camp )
635 {
636     const point_abs_omt omt = camp.camp_omt_pos().xy();
637     const overmap_with_local_coords om_loc = get_om_global( omt );
638     om_loc.om->camps.push_back( camp );
639 }
640 
seen(const tripoint_abs_omt & p)641 bool overmapbuffer::seen( const tripoint_abs_omt &p )
642 {
643     if( const overmap_with_local_coords om_loc = get_existing_om_global( p ) ) {
644         return om_loc.om->seen( om_loc.local );
645     }
646     return false;
647 }
648 
set_seen(const tripoint_abs_omt & p,bool seen)649 void overmapbuffer::set_seen( const tripoint_abs_omt &p, bool seen )
650 {
651     const overmap_with_local_coords om_loc = get_om_global( p );
652     om_loc.om->seen( om_loc.local ) = seen;
653 }
654 
ter(const tripoint_abs_omt & p)655 const oter_id &overmapbuffer::ter( const tripoint_abs_omt &p )
656 {
657     const overmap_with_local_coords om_loc = get_om_global( p );
658     return om_loc.om->ter( om_loc.local );
659 }
660 
ter_set(const tripoint_abs_omt & p,const oter_id & id)661 void overmapbuffer::ter_set( const tripoint_abs_omt &p, const oter_id &id )
662 {
663     const overmap_with_local_coords om_loc = get_om_global( p );
664     return om_loc.om->ter_set( om_loc.local, id );
665 }
666 
reveal(const point_abs_omt & center,int radius,int z)667 bool overmapbuffer::reveal( const point_abs_omt &center, int radius, int z )
668 {
669     return reveal( tripoint_abs_omt( center, z ), radius );
670 }
671 
reveal(const tripoint_abs_omt & center,int radius)672 bool overmapbuffer::reveal( const tripoint_abs_omt &center, int radius )
673 {
674     return reveal( center, radius, []( const oter_id & ) {
675         return true;
676     } );
677 }
678 
reveal(const tripoint_abs_omt & center,int radius,const std::function<bool (const oter_id &)> & filter)679 bool overmapbuffer::reveal( const tripoint_abs_omt &center, int radius,
680                             const std::function<bool( const oter_id & )> &filter )
681 {
682     int radius_squared = radius * radius;
683     bool result = false;
684     for( int i = -radius; i <= radius; i++ ) {
685         for( int j = -radius; j <= radius; j++ ) {
686             const tripoint_abs_omt p = center + point( i, j );
687             if( seen( p ) ) {
688                 continue;
689             }
690             if( trigdist && i * i + j * j > radius_squared ) {
691                 continue;
692             }
693             if( !filter( ter( p ) ) ) {
694                 continue;
695             }
696             result = true;
697             set_seen( p, true );
698         }
699     }
700     return result;
701 }
702 
get_npc_path(const tripoint_abs_omt & src,const tripoint_abs_omt & dest)703 std::vector<tripoint_abs_omt> overmapbuffer::get_npc_path( const tripoint_abs_omt &src,
704         const tripoint_abs_omt &dest )
705 {
706     path_type ptype;
707     return get_npc_path( src, dest, ptype );
708 }
709 
get_npc_path(const tripoint_abs_omt & src,const tripoint_abs_omt & dest,path_type & ptype)710 std::vector<tripoint_abs_omt> overmapbuffer::get_npc_path(
711     const tripoint_abs_omt &src, const tripoint_abs_omt &dest, path_type &ptype )
712 {
713     std::vector<tripoint_abs_omt> path;
714     static const int RADIUS = 4;            // Maximal radius of search (in overmaps)
715     static const point_rel_omt O( RADIUS * OMAPX,
716                                   RADIUS * OMAPY );   // half-height of the area to search in
717     if( src == overmap::invalid_tripoint || dest == overmap::invalid_tripoint ) {
718         return path;
719     }
720 
721     // Local source - center of the local area
722     const point_rel_omt start( O );
723     // To convert local coordinates to global ones
724     const tripoint_abs_omt base = src - start;
725     // Local destination - relative to base
726     const point_rel_omt finish = ( dest - base ).xy();
727 
728     const auto get_ter_at = [&]( const point_rel_omt & p ) {
729         return ter( base + p );
730     };
731     const auto estimate =
732     [&]( const pf::node<point_rel_omt> &cur, const pf::node<point_rel_omt> * ) {
733         const tripoint_abs_omt convert_result = base + tripoint_rel_omt( cur.pos, 0 );
734         if( ptype.only_known_by_player && !seen( convert_result ) ) {
735             return pf::rejected;
736         }
737         int res = 0;
738         const oter_id oter = get_ter_at( cur.pos );
739         int travel_cost = static_cast<int>( oter->get_travel_cost() );
740         if( ptype.avoid_danger && is_marked_dangerous( convert_result ) ) {
741             return pf::rejected;
742         }
743         if( ptype.only_road && ( !is_ot_match( "road", oter, ot_match_type::type ) &&
744                                  !is_ot_match( "bridge", oter, ot_match_type::type ) &&
745                                  !is_ot_match( "road_nesw_manhole", oter, ot_match_type::type ) ) ) {
746             return pf::rejected;
747         }
748         if( ptype.only_water && !is_river_or_lake( oter ) ) {
749             return pf::rejected;
750         }
751         if( ptype.only_air && ( !is_ot_match( "open_air", oter, ot_match_type::type ) ) ) {
752             return pf::rejected;
753         }
754         if( is_ot_match( "empty_rock", oter, ot_match_type::type ) ) {
755             return pf::rejected;
756         } else if( is_ot_match( "open_air", oter, ot_match_type::type ) ) {
757             if( ptype.only_air ) {
758                 travel_cost += 1;
759             } else {
760                 return pf::rejected;
761             }
762         } else if( is_ot_match( "forest", oter, ot_match_type::type ) ) {
763             travel_cost = 10;
764         } else if( is_ot_match( "forest_water", oter, ot_match_type::type ) ) {
765             travel_cost = 15;
766         } else if( is_ot_match( "road", oter, ot_match_type::type ) ||
767                    is_ot_match( "bridge", oter, ot_match_type::type ) ||
768                    is_ot_match( "road_nesw_manhole", oter, ot_match_type::type ) ) {
769             travel_cost = 1;
770         } else if( is_river_or_lake( oter ) ) {
771             if( ptype.amphibious || ptype.only_water ) {
772                 travel_cost = 1;
773             } else {
774                 return pf::rejected;
775             }
776         }
777         res += travel_cost;
778         res += manhattan_dist( finish, cur.pos );
779 
780         return res;
781     };
782     pf::path<point_rel_omt> route = pf::find_path( start, finish, 2 * O, estimate );
783     for( auto node : route.nodes ) {
784         tripoint_abs_omt convert_result = base + tripoint_rel_omt( node.pos, 0 );
785         convert_result.z() = base.z();
786         path.push_back( convert_result );
787     }
788     return path;
789 }
790 
reveal_route(const tripoint_abs_omt & source,const tripoint_abs_omt & dest,int radius,bool road_only)791 bool overmapbuffer::reveal_route( const tripoint_abs_omt &source, const tripoint_abs_omt &dest,
792                                   int radius, bool road_only )
793 {
794     static const int RADIUS = 4;            // Maximal radius of search (in overmaps)
795     static const point_rel_omt O( RADIUS * OMAPX,
796                                   RADIUS * OMAPY );   // half-height of the area to search in
797 
798     if( source == overmap::invalid_tripoint || dest == overmap::invalid_tripoint ) {
799         return false;
800     }
801 
802     // Local source - center of the local area
803     const point_rel_omt start( O );
804     // To convert local coordinates to global ones
805     const tripoint_abs_omt base = source - start;
806     // Local destination - relative to base
807     const point_rel_omt finish = ( dest - base ).xy();
808 
809     const auto get_ter_at = [&]( const point_rel_omt & p ) {
810         return ter( base + p );
811     };
812 
813     const oter_id oter = get_ter_at( start );
814     const auto connection = overmap_connections::guess_for( oter );
815 
816     if( !connection ) {
817         return false;
818     }
819 
820     const auto estimate =
821     [&]( const pf::node<point_rel_omt> &cur, const pf::node<point_rel_omt> * ) {
822         int res = 0;
823 
824         const oter_id oter = get_ter_at( cur.pos );
825 
826         if( !connection->has( oter ) ) {
827             if( road_only ) {
828                 return pf::rejected;
829             }
830 
831             if( is_river( oter ) ) {
832                 return pf::rejected; // Can't walk on water
833             }
834             // Allow going slightly off-road to overcome small obstacles (e.g. craters),
835             // but heavily penalize that to make roads preferable
836             res += 250;
837         }
838 
839         res += manhattan_dist( finish, cur.pos );
840 
841         return res;
842     };
843 
844     const auto path = pf::find_path( start, finish, 2 * O, estimate );
845 
846     for( const auto &node : path.nodes ) {
847         reveal( base + node.pos, radius );
848     }
849     return !path.nodes.empty();
850 }
851 
check_ot_existing(const std::string & type,ot_match_type match_type,const tripoint_abs_omt & loc)852 bool overmapbuffer::check_ot_existing( const std::string &type, ot_match_type match_type,
853                                        const tripoint_abs_omt &loc )
854 {
855     const overmap_with_local_coords om_loc = get_existing_om_global( loc );
856     if( !om_loc ) {
857         return false;
858     }
859     return om_loc.om->check_ot( type, match_type, om_loc.local );
860 }
861 
check_overmap_special_type_existing(const overmap_special_id & id,const tripoint_abs_omt & loc)862 bool overmapbuffer::check_overmap_special_type_existing(
863     const overmap_special_id &id, const tripoint_abs_omt &loc )
864 {
865     const overmap_with_local_coords om_loc = get_existing_om_global( loc );
866     if( !om_loc ) {
867         return false;
868     }
869     return om_loc.om->check_overmap_special_type( id, om_loc.local );
870 }
871 
check_ot(const std::string & type,ot_match_type match_type,const tripoint_abs_omt & p)872 bool overmapbuffer::check_ot( const std::string &type, ot_match_type match_type,
873                               const tripoint_abs_omt &p )
874 {
875     const overmap_with_local_coords om_loc = get_om_global( p );
876     return om_loc.om->check_ot( type, match_type, om_loc.local );
877 }
878 
check_overmap_special_type(const overmap_special_id & id,const tripoint_abs_omt & loc)879 bool overmapbuffer::check_overmap_special_type( const overmap_special_id &id,
880         const tripoint_abs_omt &loc )
881 {
882     const overmap_with_local_coords om_loc = get_om_global( loc );
883     return om_loc.om->check_overmap_special_type( id, om_loc.local );
884 }
885 
assign_params(const std::string & type,int const radius,bool must_be_seen,ot_match_type match_type,bool existing_overmaps_only,const cata::optional<overmap_special_id> & om_special)886 static omt_find_params assign_params(
887     const std::string &type, int const radius, bool must_be_seen,
888     ot_match_type match_type, bool existing_overmaps_only,
889     const cata::optional<overmap_special_id> &om_special )
890 {
891     omt_find_params params;
892     std::vector<std::pair<std::string, ot_match_type>> temp_types;
893     std::pair<std::string, ot_match_type> temp_pair;
894     temp_pair.first = type;
895     temp_pair.second = match_type;
896     temp_types.push_back( temp_pair );
897     params.types = temp_types;
898     params.search_range = radius;
899     params.must_see = must_be_seen;
900     params.existing_only = existing_overmaps_only;
901     params.om_special = om_special;
902     return params;
903 }
904 
is_findable_location(const tripoint_abs_omt & location,const omt_find_params & params)905 bool overmapbuffer::is_findable_location( const tripoint_abs_omt &location,
906         const omt_find_params &params )
907 {
908     bool type_matches = false;
909     if( params.existing_only ) {
910         for( const std::pair<std::string, ot_match_type> &elem : params.types ) {
911             type_matches = check_ot_existing( elem.first, elem.second, location );
912             if( type_matches ) {
913                 break;
914             }
915         }
916     } else {
917         for( const std::pair<std::string, ot_match_type> &elem : params.types ) {
918             type_matches = check_ot( elem.first, elem.second, location );
919             if( type_matches ) {
920                 break;
921             }
922         }
923     }
924     if( !type_matches ) {
925         return false;
926     }
927 
928     if( params.must_see && !seen( location ) ) {
929         return false;
930     }
931     if( params.cant_see && seen( location ) ) {
932         return false;
933     }
934 
935     if( params.om_special ) {
936         bool meets_om_special = false;
937         if( params.existing_only ) {
938             meets_om_special = check_overmap_special_type_existing( *params.om_special, location );
939         } else {
940             meets_om_special = check_overmap_special_type( *params.om_special, location );
941         }
942         if( !meets_om_special ) {
943             return false;
944         }
945     }
946 
947     return true;
948 }
949 
find_closest(const tripoint_abs_omt & origin,const std::string & type,int const radius,bool must_be_seen,ot_match_type match_type,bool existing_overmaps_only,const cata::optional<overmap_special_id> & om_special)950 tripoint_abs_omt overmapbuffer::find_closest(
951     const tripoint_abs_omt &origin, const std::string &type, int const radius, bool must_be_seen,
952     ot_match_type match_type, bool existing_overmaps_only,
953     const cata::optional<overmap_special_id> &om_special )
954 {
955     const omt_find_params params = assign_params( type, radius, must_be_seen, match_type,
956                                    existing_overmaps_only, om_special );
957     return find_closest( origin, params );
958 }
959 
find_closest(const tripoint_abs_omt & origin,const omt_find_params & params)960 tripoint_abs_omt overmapbuffer::find_closest( const tripoint_abs_omt &origin,
961         const omt_find_params &params )
962 {
963     // Check the origin before searching adjacent tiles!
964     if( params.min_distance == 0 && is_findable_location( origin, params ) ) {
965         return origin;
966     }
967 
968     // By default search overmaps within a radius of 4,
969     // i.e. C = current overmap, X = overmaps searched:
970     // XXXXXXXXX
971     // XXXXXXXXX
972     // XXXXXXXXX
973     // XXXXXXXXX
974     // XXXXCXXXX
975     // XXXXXXXXX
976     // XXXXXXXXX
977     // XXXXXXXXX
978     // XXXXXXXXX
979     //
980     // See overmap::place_specials for how we attempt to insure specials are placed within this
981     // range.  The actual number is 5 because 1 covers the current overmap,
982     // and each additional one expends the search to the next concentric circle of overmaps.
983     const int min_dist = params.min_distance;
984     const int max_dist = params.search_range ? params.search_range : OMAPX * 5;
985 
986     std::vector<tripoint_abs_omt> result;
987     cata::optional<int> found_dist;
988 
989     for( const point_abs_omt &loc_xy : closest_points_first( origin.xy(), min_dist, max_dist ) ) {
990         const int dist_xy = square_dist( origin.xy(), loc_xy );
991 
992         if( found_dist && *found_dist < dist_xy ) {
993             break;
994         }
995 
996         for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) {
997             const tripoint_abs_omt loc( loc_xy, z );
998             const int dist = square_dist( origin, loc );
999 
1000             if( found_dist && *found_dist < dist ) {
1001                 continue;
1002             }
1003 
1004             if( is_findable_location( loc, params ) ) {
1005                 found_dist = dist;
1006                 result.push_back( loc );
1007             }
1008         }
1009     }
1010 
1011     return random_entry( result, overmap::invalid_tripoint );
1012 }
1013 
find_all(const tripoint_abs_omt & origin,const omt_find_params & params)1014 std::vector<tripoint_abs_omt> overmapbuffer::find_all( const tripoint_abs_omt &origin,
1015         const omt_find_params &params )
1016 {
1017     std::vector<tripoint_abs_omt> result;
1018     // dist == 0 means search a whole overmap diameter.
1019     const int min_dist = params.min_distance;
1020     const int max_dist = params.search_range ? params.search_range : OMAPX;
1021 
1022     for( const tripoint_abs_omt &loc : closest_points_first( origin, min_dist, max_dist ) ) {
1023         if( is_findable_location( loc, params ) ) {
1024             result.push_back( loc );
1025         }
1026     }
1027 
1028     return result;
1029 }
1030 
find_all(const tripoint_abs_omt & origin,const std::string & type,int dist,bool must_be_seen,ot_match_type match_type,bool existing_overmaps_only,const cata::optional<overmap_special_id> & om_special)1031 std::vector<tripoint_abs_omt> overmapbuffer::find_all(
1032     const tripoint_abs_omt &origin, const std::string &type, int dist, bool must_be_seen,
1033     ot_match_type match_type, bool existing_overmaps_only,
1034     const cata::optional<overmap_special_id> &om_special )
1035 {
1036     const omt_find_params params = assign_params( type, dist, must_be_seen, match_type,
1037                                    existing_overmaps_only, om_special );
1038     return find_all( origin, params );
1039 }
1040 
find_random(const tripoint_abs_omt & origin,const omt_find_params & params)1041 tripoint_abs_omt overmapbuffer::find_random( const tripoint_abs_omt &origin,
1042         const omt_find_params &params )
1043 {
1044     return random_entry( find_all( origin, params ), overmap::invalid_tripoint );
1045 }
1046 
find_random(const tripoint_abs_omt & origin,const std::string & type,int dist,bool must_be_seen,ot_match_type match_type,bool existing_overmaps_only,const cata::optional<overmap_special_id> & om_special)1047 tripoint_abs_omt overmapbuffer::find_random(
1048     const tripoint_abs_omt &origin, const std::string &type, int dist, bool must_be_seen,
1049     ot_match_type match_type, bool existing_overmaps_only,
1050     const cata::optional<overmap_special_id> &om_special )
1051 {
1052     const omt_find_params params = assign_params( type, dist, must_be_seen, match_type,
1053                                    existing_overmaps_only, om_special );
1054     return find_random( origin, params );
1055 }
1056 
find_npc(character_id id)1057 shared_ptr_fast<npc> overmapbuffer::find_npc( character_id id )
1058 {
1059     for( auto &it : overmaps ) {
1060         if( auto p = it.second->find_npc( id ) ) {
1061             return p;
1062         }
1063     }
1064     return nullptr;
1065 }
1066 
find_camp(const point_abs_omt & p)1067 cata::optional<basecamp *> overmapbuffer::find_camp( const point_abs_omt &p )
1068 {
1069     for( auto &it : overmaps ) {
1070         const point_abs_omt p2( p );
1071         for( int x2 = p2.x() - 3; x2 < p2.x() + 3; x2++ ) {
1072             for( int y2 = p2.y() - 3; y2 < p2.y() + 3; y2++ ) {
1073                 if( cata::optional<basecamp *> camp = it.second->find_camp( point_abs_omt( x2, y2 ) ) ) {
1074                     return camp;
1075                 }
1076             }
1077         }
1078     }
1079     return cata::nullopt;
1080 }
1081 
insert_npc(const shared_ptr_fast<npc> & who)1082 void overmapbuffer::insert_npc( const shared_ptr_fast<npc> &who )
1083 {
1084     cata_assert( who );
1085     const tripoint_abs_omt npc_omt_pos = who->global_omt_location();
1086     const point_abs_om npc_om_pos = project_to<coords::om>( npc_omt_pos.xy() );
1087     get( npc_om_pos ).insert_npc( who );
1088 }
1089 
remove_npc(const character_id & id)1090 shared_ptr_fast<npc> overmapbuffer::remove_npc( const character_id &id )
1091 {
1092     for( auto &it : overmaps ) {
1093         if( const auto p = it.second->erase_npc( id ) ) {
1094             return p;
1095         }
1096     }
1097     debugmsg( "overmapbuffer::remove_npc: NPC (%d) not found.", id.get_value() );
1098     return nullptr;
1099 }
1100 
get_npcs_near_player(int radius)1101 std::vector<shared_ptr_fast<npc>> overmapbuffer::get_npcs_near_player( int radius )
1102 {
1103     tripoint_abs_omt plpos_omt = get_player_character().global_omt_location();
1104     // get_npcs_near needs submap coordinates
1105     tripoint_abs_sm plpos = project_to<coords::sm>( plpos_omt );
1106     // INT_MIN is a (a bit ugly) way to inform get_npcs_near not to filter by z-level
1107     const int zpos = get_map().has_zlevels() ? INT_MIN : plpos.z();
1108     return get_npcs_near( tripoint_abs_sm( plpos.xy(), zpos ), radius );
1109 }
1110 
get_overmaps_near(const tripoint_abs_sm & location,const int radius)1111 std::vector<overmap *> overmapbuffer::get_overmaps_near( const tripoint_abs_sm &location,
1112         const int radius )
1113 {
1114     // Grab the corners of a square around the target location at distance radius.
1115     // Convert to overmap coordinates and iterate from the minimum to the maximum.
1116     const point_abs_om start =
1117         project_to<coords::om>( location.xy() + point( -radius, -radius ) );
1118     const point_abs_om end =
1119         project_to<coords::om>( location.xy() + point( radius, radius ) );
1120     const point_rel_om offset = end - start;
1121 
1122     std::vector<overmap *> result;
1123     result.reserve( static_cast<size_t>( offset.x() + 1 ) * static_cast<size_t>( offset.y() + 1 ) );
1124 
1125     for( int x = start.x(); x <= end.x(); ++x ) {
1126         for( int y = start.y(); y <= end.y(); ++y ) {
1127             if( overmap *existing_om = get_existing( point_abs_om( x, y ) ) ) {
1128                 result.emplace_back( existing_om );
1129             }
1130         }
1131     }
1132 
1133     // Sort the resulting overmaps so that the closest ones are first.
1134     const tripoint_abs_om center = project_to<coords::om>( location );
1135     std::sort( result.begin(), result.end(), [&center]( const overmap * lhs,
1136     const overmap * rhs ) {
1137         const tripoint_abs_om lhs_pos( lhs->pos(), 0 );
1138         const tripoint_abs_om rhs_pos( rhs->pos(), 0 );
1139         return trig_dist( center, lhs_pos ) < trig_dist( center, rhs_pos );
1140     } );
1141 
1142     return result;
1143 }
1144 
get_overmaps_near(const point_abs_sm & p,const int radius)1145 std::vector<overmap *> overmapbuffer::get_overmaps_near( const point_abs_sm &p, const int radius )
1146 {
1147     return get_overmaps_near( tripoint_abs_sm( p, 0 ), radius );
1148 }
1149 
get_companion_mission_npcs(int range)1150 std::vector<shared_ptr_fast<npc>> overmapbuffer::get_companion_mission_npcs( int range )
1151 {
1152     std::vector<shared_ptr_fast<npc>> available;
1153     // TODO: this is an arbitrary radius, replace with something sane.
1154     for( const auto &guy : get_npcs_near_player( range ) ) {
1155         if( guy->has_companion_mission() ) {
1156             available.push_back( guy );
1157         }
1158     }
1159     return available;
1160 }
1161 
1162 // If z == INT_MIN, allow all z-levels
get_npcs_near(const tripoint_abs_sm & p,int radius)1163 std::vector<shared_ptr_fast<npc>> overmapbuffer::get_npcs_near( const tripoint_abs_sm &p,
1164                                int radius )
1165 {
1166     std::vector<shared_ptr_fast<npc>> result;
1167     for( auto &it : get_overmaps_near( p.xy(), radius ) ) {
1168         auto temp = it->get_npcs( [&]( const npc & guy ) {
1169             // Global position of NPC, in submap coordinates
1170             // TODO: fix point types
1171             const tripoint_abs_sm pos( guy.global_sm_location() );
1172             if( p.z() != INT_MIN && pos.z() != p.z() ) {
1173                 return false;
1174             }
1175             return square_dist( p.xy(), pos.xy() ) <= radius;
1176         } );
1177         result.insert( result.end(), temp.begin(), temp.end() );
1178     }
1179     return result;
1180 }
1181 
1182 // If z == INT_MIN, allow all z-levels
get_npcs_near_omt(const tripoint_abs_omt & p,int radius)1183 std::vector<shared_ptr_fast<npc>> overmapbuffer::get_npcs_near_omt( const tripoint_abs_omt &p,
1184                                int radius )
1185 {
1186     std::vector<shared_ptr_fast<npc>> result;
1187     for( auto &it : get_overmaps_near( project_to<coords::sm>( p.xy() ), radius ) ) {
1188         auto temp = it->get_npcs( [&]( const npc & guy ) {
1189             // Global position of NPC, in submap coordinates
1190             tripoint_abs_omt pos = guy.global_omt_location();
1191             if( p.z() != INT_MIN && pos.z() != p.z() ) {
1192                 return false;
1193             }
1194             return square_dist( p.xy(), pos.xy() ) <= radius;
1195         } );
1196         result.insert( result.end(), temp.begin(), temp.end() );
1197     }
1198     return result;
1199 }
1200 
create_radio_tower_reference(const overmap & om,radio_tower & t,const tripoint_abs_sm & center)1201 static radio_tower_reference create_radio_tower_reference( const overmap &om, radio_tower &t,
1202         const tripoint_abs_sm &center )
1203 {
1204     // global submap coordinates, same as center is
1205     const point_abs_sm pos = project_combine( om.pos(), t.pos );
1206     const int strength = t.strength - rl_dist( tripoint_abs_sm( pos, 0 ), center );
1207     return radio_tower_reference{ &t, pos, strength };
1208 }
1209 
find_radio_station(const int frequency)1210 radio_tower_reference overmapbuffer::find_radio_station( const int frequency )
1211 {
1212     // TODO: fix point types
1213     const tripoint_abs_sm center( get_player_character().global_sm_location() );
1214     for( auto &om : get_overmaps_near( center, RADIO_MAX_STRENGTH ) ) {
1215         for( auto &tower : om->radios ) {
1216             const radio_tower_reference rref = create_radio_tower_reference( *om, tower, center );
1217             if( rref.signal_strength > 0 && tower.frequency == frequency ) {
1218                 return rref;
1219             }
1220         }
1221     }
1222     return radio_tower_reference{ nullptr, point_abs_sm(), 0 };
1223 }
1224 
find_all_radio_stations()1225 std::vector<radio_tower_reference> overmapbuffer::find_all_radio_stations()
1226 {
1227     std::vector<radio_tower_reference> result;
1228     // TODO: fix point types
1229     const tripoint_abs_sm center( get_player_character().global_sm_location() );
1230     // perceived signal strength is distance (in submaps) - signal strength, so towers
1231     // further than RADIO_MAX_STRENGTH submaps away can never be received at all.
1232     const int radius = RADIO_MAX_STRENGTH;
1233     for( auto &om : get_overmaps_near( center, radius ) ) {
1234         for( auto &tower : om->radios ) {
1235             const radio_tower_reference rref = create_radio_tower_reference( *om, tower, center );
1236             if( rref.signal_strength > 0 ) {
1237                 result.push_back( rref );
1238             }
1239         }
1240     }
1241     return result;
1242 }
1243 
get_camps_near(const tripoint_abs_sm & location,int radius)1244 std::vector<camp_reference> overmapbuffer::get_camps_near( const tripoint_abs_sm &location,
1245         int radius )
1246 {
1247     std::vector<camp_reference> result;
1248     for( overmap *om : get_overmaps_near( location, radius ) ) {
1249         result.reserve( result.size() + om->camps.size() );
1250         std::transform( om->camps.begin(), om->camps.end(), std::back_inserter( result ),
1251         [&]( basecamp & element ) {
1252             const tripoint_abs_omt camp_pt = element.camp_omt_pos();
1253             const tripoint_abs_sm camp_sm = project_to<coords::sm>( camp_pt );
1254             const int distance = rl_dist( camp_sm, location );
1255 
1256             return camp_reference{ &element, camp_sm, distance };
1257         } );
1258     }
1259     std::sort( result.begin(), result.end(), []( const camp_reference & lhs,
1260     const camp_reference & rhs ) {
1261         return lhs.get_distance_from_bounds() < rhs.get_distance_from_bounds();
1262     } );
1263     return result;
1264 }
1265 
get_overmap_npcs()1266 std::vector<shared_ptr_fast<npc>> overmapbuffer::get_overmap_npcs()
1267 {
1268     std::vector<shared_ptr_fast<npc>> result;
1269     for( auto &om : overmaps ) {
1270         const overmap &overmap = *om.second;
1271         for( const auto &guy : overmap.npcs ) {
1272             result.push_back( guy );
1273         }
1274     }
1275     return result;
1276 }
1277 
get_cities_near(const tripoint_abs_sm & location,int radius)1278 std::vector<city_reference> overmapbuffer::get_cities_near( const tripoint_abs_sm &location,
1279         int radius )
1280 {
1281     std::vector<city_reference> result;
1282 
1283     for( overmap *om : get_overmaps_near( location, radius ) ) {
1284         result.reserve( result.size() + om->cities.size() );
1285         std::transform( om->cities.begin(), om->cities.end(), std::back_inserter( result ),
1286         [&]( city & element ) {
1287             const auto rel_pos_city = project_to<coords::sm>( element.pos );
1288             const auto abs_pos_city =
1289                 tripoint_abs_sm( project_combine( om->pos(), rel_pos_city ), 0 );
1290             const int distance = rl_dist( abs_pos_city, location );
1291 
1292             return city_reference{ &element, abs_pos_city, distance };
1293         } );
1294     }
1295 
1296     std::sort( result.begin(), result.end(), []( const city_reference & lhs,
1297     const city_reference & rhs ) {
1298         return lhs.get_distance_from_bounds() < rhs.get_distance_from_bounds();
1299     } );
1300 
1301     return result;
1302 }
1303 
closest_city(const tripoint_abs_sm & center)1304 city_reference overmapbuffer::closest_city( const tripoint_abs_sm &center )
1305 {
1306     const auto cities = get_cities_near( center, omt_to_sm_copy( OMAPX ) );
1307 
1308     if( !cities.empty() ) {
1309         return cities.front();
1310     }
1311 
1312     return city_reference::invalid;
1313 }
1314 
closest_known_city(const tripoint_abs_sm & center)1315 city_reference overmapbuffer::closest_known_city( const tripoint_abs_sm &center )
1316 {
1317     const auto cities = get_cities_near( center, omt_to_sm_copy( OMAPX ) );
1318     const auto it = std::find_if( cities.begin(), cities.end(),
1319     [this]( const city_reference & elem ) {
1320         const tripoint_abs_omt p = project_to<coords::omt>( elem.abs_sm_pos );
1321         return seen( p );
1322     } );
1323 
1324     if( it != cities.end() ) {
1325         return *it;
1326     }
1327 
1328     return city_reference::invalid;
1329 }
1330 
get_description_at(const tripoint_abs_sm & where)1331 std::string overmapbuffer::get_description_at( const tripoint_abs_sm &where )
1332 {
1333     const auto oter = ter( project_to<coords::omt>( where ) );
1334     const nc_color ter_color = oter->get_color();
1335     const std::string ter_name = colorize( oter->get_name(), ter_color );
1336 
1337     if( where.z() != 0 ) {
1338         return ter_name;
1339     }
1340 
1341     const city_reference closest_cref = closest_known_city( where );
1342 
1343     if( !closest_cref ) {
1344         return ter_name;
1345     }
1346 
1347     const auto &closest_city = *closest_cref.city;
1348     const std::string closest_city_name = colorize( closest_city.name, c_yellow );
1349     const direction dir = direction_from( closest_cref.abs_sm_pos, where );
1350     const std::string dir_name = colorize( direction_name( dir ), c_light_gray );
1351 
1352     const int sm_size = omt_to_sm_copy( closest_cref.city->size );
1353     const int sm_dist = closest_cref.distance;
1354 
1355     //~ First parameter is a terrain name, second parameter is a direction, and third parameter is a city name.
1356     std::string format_string = pgettext( "terrain description", "%1$s %2$s from %3$s" );
1357     if( sm_dist <= 3 * sm_size / 4 ) {
1358         if( sm_size >= 16 ) {
1359             // The city is big enough to be split in districts.
1360             if( sm_dist <= sm_size / 4 ) {
1361                 //~ First parameter is a terrain name, second parameter is a direction, and third parameter is a city name.
1362                 format_string = pgettext( "terrain description", "%1$s in central %3$s" );
1363             } else {
1364                 //~ First parameter is a terrain name, second parameter is a direction, and third parameter is a city name.
1365                 format_string = pgettext( "terrain description", "%1$s in %2$s %3$s" );
1366             }
1367         } else {
1368             //~ First parameter is a terrain name, second parameter is a direction, and third parameter is a city name.
1369             format_string = pgettext( "terrain description", "%1$s in %3$s" );
1370         }
1371     } else if( sm_dist <= sm_size ) {
1372         if( sm_size >= 8 ) {
1373             // The city is big enough to have outskirts.
1374             //~ First parameter is a terrain name, second parameter is a direction, and third parameter is a city name.
1375             format_string = pgettext( "terrain description", "%1$s on the %2$s outskirts of %3$s" );
1376         } else {
1377             //~ First parameter is a terrain name, second parameter is a direction, and third parameter is a city name.
1378             format_string = pgettext( "terrain description", "%1$s in %3$s" );
1379         }
1380     }
1381 
1382     return string_format( format_string, ter_name, dir_name, closest_city_name );
1383 }
1384 
spawn_monster(const tripoint_abs_sm & p)1385 void overmapbuffer::spawn_monster( const tripoint_abs_sm &p )
1386 {
1387     // Create a copy, so we can reuse x and y later
1388     point_abs_sm abs_sm = p.xy();
1389     point_om_sm sm;
1390     point_abs_om omp;
1391     std::tie( omp, sm ) = project_remain<coords::om>( abs_sm );
1392     overmap &om = get( omp );
1393     const tripoint_om_sm current_submap_loc( sm, p.z() );
1394     auto monster_bucket = om.monster_map.equal_range( current_submap_loc );
1395     std::for_each( monster_bucket.first, monster_bucket.second,
1396     [&]( std::pair<const tripoint_om_sm, monster> &monster_entry ) {
1397         monster &this_monster = monster_entry.second;
1398         // The absolute position in map squares, (x,y) is already global, but it's a
1399         // submap coordinate, so translate it and add the exact monster position on
1400         // the submap. modulo because the zombies position might be negative, as it
1401         // is stored *after* it has gone out of bounds during shifting. When reloading
1402         // we only need the part that tells where on the submap to put it.
1403         point ms( modulo( this_monster.posx(), SEEX ), modulo( this_monster.posy(), SEEY ) );
1404         cata_assert( ms.x >= 0 && ms.x < SEEX );
1405         cata_assert( ms.y >= 0 && ms.y < SEEX );
1406         // TODO: fix point types
1407         ms += project_to<coords::ms>( p.xy() ).raw();
1408         const map &here = get_map();
1409         // The monster position must be local to the main map when added to the game
1410         const tripoint local = tripoint( here.getlocal( ms ), p.z() );
1411         cata_assert( here.inbounds( local ) );
1412         monster *const placed = g->place_critter_around( make_shared_fast<monster>( this_monster ),
1413                                 local, 0, true );
1414         if( placed ) {
1415             placed->on_load();
1416         }
1417     } );
1418     om.monster_map.erase( current_submap_loc );
1419 }
1420 
despawn_monster(const monster & critter)1421 void overmapbuffer::despawn_monster( const monster &critter )
1422 {
1423     // Get absolute coordinates of the monster in map squares, translate to submap position
1424     // TODO: fix point types
1425     tripoint_abs_sm abs_sm( ms_to_sm_copy( get_map().getabs( critter.pos() ) ) );
1426     // Get the overmap coordinates and get the overmap, sm is now local to that overmap
1427     point_abs_om omp;
1428     tripoint_om_sm sm;
1429     std::tie( omp, sm ) = project_remain<coords::om>( abs_sm );
1430     overmap &om = get( omp );
1431     // Store the monster using coordinates local to the overmap.
1432     om.monster_map.insert( std::make_pair( sm, critter ) );
1433 }
1434 
get_notes(int z,const std::string * pattern)1435 overmapbuffer::t_notes_vector overmapbuffer::get_notes( int z, const std::string *pattern )
1436 {
1437     t_notes_vector result;
1438     for( auto &it : overmaps ) {
1439         const overmap &om = *it.second;
1440         for( int i = 0; i < OMAPX; i++ ) {
1441             for( int j = 0; j < OMAPY; j++ ) {
1442                 tripoint_om_omt p( i, j, z );
1443                 const std::string &note = om.note( p );
1444                 if( note.empty() ) {
1445                     continue;
1446                 }
1447                 if( pattern != nullptr && lcmatch( note, *pattern ) ) {
1448                     // pattern not found in note text
1449                     continue;
1450                 }
1451                 result.push_back( t_point_with_note(
1452                                       project_combine( om.pos(), p.xy() ),
1453                                       om.note( p )
1454                                   ) );
1455             }
1456         }
1457     }
1458     return result;
1459 }
1460 
get_extras(int z,const std::string * pattern)1461 overmapbuffer::t_extras_vector overmapbuffer::get_extras( int z, const std::string *pattern )
1462 {
1463     overmapbuffer::t_extras_vector result;
1464     for( auto &it : overmaps ) {
1465         const overmap &om = *it.second;
1466         for( int i = 0; i < OMAPX; i++ ) {
1467             for( int j = 0; j < OMAPY; j++ ) {
1468                 tripoint_om_omt p( i, j, z );
1469                 const string_id<map_extra> &extra = om.extra( p );
1470                 if( extra.is_null() ) {
1471                     continue;
1472                 }
1473                 const std::string &extra_text = extra.c_str();
1474                 if( pattern != nullptr && lcmatch( extra_text, *pattern ) ) {
1475                     // pattern not found in note text
1476                     continue;
1477                 }
1478                 result.push_back( t_point_with_extra(
1479                                       project_combine( om.pos(), p.xy() ),
1480                                       om.extra( p )
1481                                   ) );
1482             }
1483         }
1484     }
1485     return result;
1486 }
1487 
is_safe(const tripoint_abs_omt & p)1488 bool overmapbuffer::is_safe( const tripoint_abs_omt &p )
1489 {
1490     for( auto &mongrp : monsters_at( p ) ) {
1491         if( !mongrp->is_safe() ) {
1492             return false;
1493         }
1494     }
1495     return true;
1496 }
1497 
place_special(const overmap_special & special,const tripoint_abs_omt & p,om_direction::type dir,const bool must_be_unexplored,const bool force)1498 bool overmapbuffer::place_special(
1499     const overmap_special &special, const tripoint_abs_omt &p, om_direction::type dir,
1500     const bool must_be_unexplored, const bool force )
1501 {
1502     const overmap_with_local_coords om_loc = get_om_global( p );
1503 
1504     bool placed = false;
1505     // Only place this special if we can actually place it per its criteria, or we're forcing
1506     // the placement, which is mostly a debug behavior, since a forced placement may not function
1507     // correctly (e.g. won't check correct underlying terrain).
1508 
1509     if( om_loc.om->can_place_special(
1510             special, om_loc.local, dir, must_be_unexplored ) || force ) {
1511         // Get the closest city that is within the overmap because
1512         // all of the overmap generation functions only function within
1513         // the single overmap. If future generation is hoisted up to the
1514         // buffer to spawn overmaps, then this can also be changed accordingly.
1515         const city c = om_loc.om->get_nearest_city( om_loc.local );
1516         om_loc.om->place_special( special, om_loc.local, dir, c,
1517                                   must_be_unexplored, force );
1518         placed = true;
1519     }
1520     return placed;
1521 }
1522 
place_special(const overmap_special_id & special_id,const tripoint_abs_omt & center,int radius)1523 bool overmapbuffer::place_special( const overmap_special_id &special_id,
1524                                    const tripoint_abs_omt &center, int radius )
1525 {
1526     // First find the requested special. If it doesn't exist, we're done here.
1527     bool found = false;
1528     overmap_special special;
1529     for( const auto &elem : overmap_specials::get_all() ) {
1530         if( elem.id == special_id ) {
1531             special = elem;
1532             found = true;
1533             break;
1534         }
1535     }
1536     if( !found ) {
1537         return false;
1538     }
1539 
1540     // Force our special to occur just once when we're spawning it here.
1541     special.occurrences.min = 1;
1542     special.occurrences.max = 1;
1543 
1544     // Figure out the longest side of the special for purposes of determining our sector size
1545     // when attempting placements.
1546     const auto calculate_longest_side = [&special]() {
1547         auto min_max_x = std::minmax_element( special.terrains.begin(),
1548                                               special.terrains.end(), []( const overmap_special_terrain & lhs,
1549         const overmap_special_terrain & rhs ) {
1550             return lhs.p.x < rhs.p.x;
1551         } );
1552 
1553         auto min_max_y = std::minmax_element( special.terrains.begin(),
1554                                               special.terrains.end(), []( const overmap_special_terrain & lhs,
1555         const overmap_special_terrain & rhs ) {
1556             return lhs.p.y < rhs.p.y;
1557         } );
1558 
1559         const int special_longest_side = std::max( min_max_x.second->p.x - min_max_x.first->p.x,
1560                                          min_max_y.second->p.y - min_max_y.first->p.y ) + 1;
1561 
1562         // If our longest side is greater than the OMSPEC_FREQ, just use that instead.
1563         return std::min( special_longest_side, OMSPEC_FREQ );
1564     };
1565 
1566     const int longest_side = calculate_longest_side();
1567 
1568     // Predefine our sectors to search in.
1569     om_special_sectors sectors = get_sectors( longest_side );
1570 
1571     // Get all of the overmaps within the defined radius of the center.
1572     for( const auto &om : get_overmaps_near(
1573              project_to<coords::sm>( center ), omt_to_sm_copy( radius ) ) ) {
1574 
1575         // Build an overmap_special_batch for the special on this overmap.
1576         std::vector<const overmap_special *> specials;
1577         specials.push_back( &special );
1578         overmap_special_batch batch( om->pos(), specials );
1579 
1580         // Filter the sectors to those which are in in range of our center point, so
1581         // that we don't end up creating specials in areas that are outside of our radius,
1582         // since the whole point is to create a special that is within the parameters.
1583         std::vector<point_om_omt> sector_points_in_range;
1584         std::copy_if( sectors.sectors.begin(), sectors.sectors.end(),
1585         std::back_inserter( sector_points_in_range ), [&]( point_om_omt & p ) {
1586             const point_abs_omt global_sector_point = project_combine( om->pos(), p );
1587             // We'll include this sector if it's within our radius. We reduce the radius by
1588             // the length of the longest side of our special so that we don't end up in a
1589             // scenario where one overmap terrain of the special is within the radius but the
1590             // rest of it is outside the radius (due to size, rotation, etc), which would
1591             // then result in us placing the special but then not finding it later if we
1592             // search using the same radius value we used in placing it.
1593             return square_dist( global_sector_point, center.xy() ) <= radius - longest_side;
1594         } );
1595         om_special_sectors sectors_in_range {sector_points_in_range, sectors.sector_width};
1596 
1597         // Attempt to place the specials using our batch and sectors. We
1598         // require they be placed in unexplored terrain right now.
1599         om->place_specials_pass( batch, sectors_in_range, true, true );
1600 
1601         // The place special pass will erase specials that have reached their
1602         // maximum number of instances so first check if its been erased.
1603         if( batch.empty() ) {
1604             return true;
1605         }
1606 
1607         // Hasn't been erased, so lets check placement counts.
1608         for( const auto &special_placement : batch ) {
1609             if( special_placement.instances_placed > 0 ) {
1610                 // It was placed, lets get outta here.
1611                 return true;
1612             }
1613         }
1614     }
1615 
1616     // If we got this far, we've failed to make the placement.
1617     return false;
1618 }
1619