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 ¢er, 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 ¢er, 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 ¢er, 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 ¢er, 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 ¶ms )
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 ¶ms )
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 ¶ms )
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 ¶ms )
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(), [¢er]( 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 ¢er )
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 ¢er )
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 ¢er )
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 ¬e = 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 ¢er, 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