1 #include "mission.h"
2
3 #include <algorithm>
4 #include <cstdlib>
5 #include <istream>
6 #include <iterator>
7 #include <list>
8 #include <memory>
9 #include <new>
10 #include <numeric>
11 #include <set>
12 #include <unordered_map>
13 #include <utility>
14
15 #include "avatar.h"
16 #include "colony.h"
17 #include "creature.h"
18 #include "debug.h"
19 #include "dialogue_chatbin.h"
20 #include "enum_conversions.h"
21 #include "game.h"
22 #include "inventory.h"
23 #include "item.h"
24 #include "item_contents.h"
25 #include "item_group.h"
26 #include "item_stack.h"
27 #include "kill_tracker.h"
28 #include "map.h"
29 #include "map_iterator.h"
30 #include "monster.h"
31 #include "npc.h"
32 #include "npc_class.h"
33 #include "overmap.h"
34 #include "overmapbuffer.h"
35 #include "point.h"
36 #include "requirements.h"
37 #include "string_formatter.h"
38 #include "translations.h"
39 #include "vehicle.h"
40 #include "vpart_position.h"
41
42 #define dbg(x) DebugLog((x),D_GAME) << __FILE__ << ":" << __LINE__ << ": "
43
create(const character_id & npc_id) const44 mission mission_type::create( const character_id &npc_id ) const
45 {
46 mission ret;
47 ret.uid = g->assign_mission_id();
48 ret.type = this;
49 ret.npc_id = npc_id;
50 ret.item_id = item_id;
51 ret.item_count = item_count;
52 ret.value = value;
53 ret.follow_up = follow_up;
54 ret.monster_species = monster_species;
55 ret.monster_type = monster_type;
56 ret.monster_kill_goal = monster_kill_goal;
57
58 if( deadline_low != 0_turns || deadline_high != 0_turns ) {
59 ret.deadline = calendar::turn + rng( deadline_low, deadline_high );
60 } else {
61 ret.deadline = calendar::turn_zero;
62 }
63
64 return ret;
65 }
66
tname() const67 std::string mission_type::tname() const
68 {
69 return name.translated();
70 }
71
72 static std::unordered_map<int, mission> world_missions;
73
reserve_new(const mission_type_id & type,const character_id & npc_id)74 mission *mission::reserve_new( const mission_type_id &type, const character_id &npc_id )
75 {
76 const mission tmp = mission_type::get( type )->create( npc_id );
77 // TODO: Warn about overwrite?
78 mission &miss = world_missions[tmp.uid] = tmp;
79 return &miss;
80 }
81
find(int id)82 mission *mission::find( int id )
83 {
84 const auto iter = world_missions.find( id );
85 if( iter != world_missions.end() ) {
86 return &iter->second;
87 }
88 dbg( D_ERROR ) << "requested mission with uid " << id << " does not exist";
89 debugmsg( "requested mission with uid %d does not exist", id );
90 return nullptr;
91 }
92
get_all_active()93 std::vector<mission *> mission::get_all_active()
94 {
95 std::vector<mission *> ret;
96 ret.reserve( world_missions.size() );
97 for( auto &pr : world_missions ) {
98 ret.push_back( &pr.second );
99 }
100
101 return ret;
102 }
103
add_existing(const mission & m)104 void mission::add_existing( const mission &m )
105 {
106 world_missions[ m.uid ] = m;
107 }
108
process_all()109 void mission::process_all()
110 {
111 for( auto &e : world_missions ) {
112 e.second.process();
113 }
114 }
115
to_ptr_vector(const std::vector<int> & vec)116 std::vector<mission *> mission::to_ptr_vector( const std::vector<int> &vec )
117 {
118 std::vector<mission *> result;
119 for( const int &id : vec ) {
120 mission *miss = find( id );
121 if( miss != nullptr ) {
122 result.push_back( miss );
123 }
124 }
125 return result;
126 }
127
to_uid_vector(const std::vector<mission * > & vec)128 std::vector<int> mission::to_uid_vector( const std::vector<mission *> &vec )
129 {
130 std::vector<int> result;
131 result.reserve( vec.size() );
132 for( const mission *miss : vec ) {
133 result.push_back( miss->uid );
134 }
135 return result;
136 }
137
clear_all()138 void mission::clear_all()
139 {
140 world_missions.clear();
141 }
142
on_creature_death(Creature & poor_dead_dude)143 void mission::on_creature_death( Creature &poor_dead_dude )
144 {
145 if( poor_dead_dude.is_hallucination() ) {
146 return;
147 }
148 monster *mon = dynamic_cast<monster *>( &poor_dead_dude );
149 if( mon != nullptr ) {
150 if( mon->mission_ids.empty() ) {
151 return;
152 }
153 for( const int mission_id : mon->mission_ids ) {
154 mission *found_mission = mission::find( mission_id );
155 if( !found_mission ) {
156 debugmsg( "invalid mission id %d", mission_id );
157 continue;
158 }
159 const mission_type *type = found_mission->type;
160 if( type->goal == MGOAL_FIND_MONSTER ) {
161 found_mission->fail();
162 }
163 if( type->goal == MGOAL_KILL_MONSTER ) {
164 found_mission->step_complete( 1 );
165 }
166 }
167 return;
168 }
169 npc *p = dynamic_cast<npc *>( &poor_dead_dude );
170 if( p == nullptr ) {
171 // Must be the player
172 for( auto &miss : get_avatar().get_active_missions() ) {
173 // mission is free and can be reused
174 miss->player_id = character_id();
175 }
176 // The missions remains assigned to the (dead) character. This should not cause any problems
177 // as the character is dismissed anyway.
178 // Technically, the active missions could be moved to the failed mission section.
179 return;
180 }
181 const character_id dead_guys_id = p->getID();
182 for( auto &e : world_missions ) {
183 mission &i = e.second;
184 if( !i.in_progress() ) {
185 continue;
186 }
187 //complete the mission if you needed killing
188 if( i.type->goal == MGOAL_ASSASSINATE && i.target_npc_id == dead_guys_id ) {
189 i.step_complete( 1 );
190 }
191 //fail the mission if the mission giver dies
192 if( i.npc_id == dead_guys_id ) {
193 i.fail();
194 }
195 //fail the mission if recruit target dies
196 if( i.type->goal == MGOAL_RECRUIT_NPC && i.target_npc_id == dead_guys_id ) {
197 i.fail();
198 }
199 }
200 }
201
on_creature_fusion(Creature & fuser,Creature & fused)202 bool mission::on_creature_fusion( Creature &fuser, Creature &fused )
203 {
204 if( fuser.is_hallucination() || fused.is_hallucination() ) {
205 return false;
206 }
207 monster *mon_fuser = dynamic_cast<monster *>( &fuser );
208 if( mon_fuser == nullptr ) {
209 debugmsg( "Unimplemented: fuser is not a monster" );
210 return false;
211 }
212 monster *mon_fused = dynamic_cast<monster *>( &fused );
213 if( mon_fused == nullptr ) {
214 debugmsg( "Unimplemented: fused is not a monster" );
215 return false;
216 }
217 bool mission_transfered = false;
218 for( const int mission_id : mon_fused->mission_ids ) {
219 const mission *const found_mission = mission::find( mission_id );
220 if( !found_mission ) {
221 debugmsg( "invalid mission id %d", mission_id );
222 continue;
223 }
224 const mission_type *const type = found_mission->type;
225 if( type->goal == MGOAL_KILL_MONSTER ) {
226 // the fuser has to be killed now!
227 mon_fuser->mission_ids.emplace( mission_id );
228 mon_fused->mission_ids.erase( mission_id );
229 mission_transfered = true;
230 }
231 }
232 return mission_transfered;
233 }
234
on_talk_with_npc(const character_id & npc_id)235 void mission::on_talk_with_npc( const character_id &npc_id )
236 {
237 switch( type->goal ) {
238 case MGOAL_TALK_TO_NPC:
239 // If our goal is to talk to this npc, and we haven't yet completed a step for this
240 // mission, then complete a step.
241 if( npc_id == target_npc_id && step == 0 ) {
242 step_complete( 1 );
243 }
244 break;
245 default:
246 break;
247 }
248 }
249
reserve_random(const mission_origin origin,const tripoint_abs_omt & p,const character_id & npc_id)250 mission *mission::reserve_random( const mission_origin origin, const tripoint_abs_omt &p,
251 const character_id &npc_id )
252 {
253 const auto type = mission_type::get_random_id( origin, p );
254 if( type.is_null() ) {
255 return nullptr;
256 }
257 return mission::reserve_new( type, npc_id );
258 }
259
assign(avatar & u)260 void mission::assign( avatar &u )
261 {
262 if( player_id == u.getID() ) {
263 debugmsg( "strange: player is already assigned to mission %d", uid );
264 return;
265 }
266 if( player_id.is_valid() ) {
267 debugmsg( "tried to assign mission %d to player, but mission is already assigned to %d",
268 uid, player_id.get_value() );
269 return;
270 }
271 player_id = u.getID();
272 u.on_mission_assignment( *this );
273 if( status == mission_status::yet_to_start ) {
274 const kill_tracker &kills = g->get_kill_tracker();
275 if( type->goal == MGOAL_KILL_MONSTER_TYPE && monster_type != mtype_id::NULL_ID() ) {
276 kill_count_to_reach = kills.kill_count( monster_type ) + monster_kill_goal;
277 } else if( type->goal == MGOAL_KILL_MONSTER_SPEC ) {
278 kill_count_to_reach = kills.kill_count( monster_species ) + monster_kill_goal;
279 }
280 if( type->deadline_low != 0_turns || type->deadline_high != 0_turns ) {
281 deadline = calendar::turn + rng( type->deadline_low, type->deadline_high );
282 } else {
283 deadline = calendar::turn_zero;
284 }
285 type->start( this );
286 status = mission_status::in_progress;
287 }
288 }
289
fail()290 void mission::fail()
291 {
292 status = mission_status::failure;
293 avatar &player_character = get_avatar();
294 if( player_character.getID() == player_id ) {
295 player_character.on_mission_finished( *this );
296 }
297
298 type->fail( this );
299 }
300
set_target_to_mission_giver()301 void mission::set_target_to_mission_giver()
302 {
303 const npc *giver = g->find_npc( npc_id );
304 if( giver != nullptr ) {
305 target = giver->global_omt_location();
306 } else {
307 target = overmap::invalid_tripoint;
308 }
309 }
310
step_complete(const int _step)311 void mission::step_complete( const int _step )
312 {
313 step = _step;
314 switch( type->goal ) {
315 case MGOAL_FIND_ITEM:
316 case MGOAL_FIND_ITEM_GROUP:
317 case MGOAL_FIND_MONSTER:
318 case MGOAL_ASSASSINATE:
319 case MGOAL_KILL_MONSTER:
320 case MGOAL_COMPUTER_TOGGLE:
321 case MGOAL_TALK_TO_NPC:
322 // Go back and report.
323 set_target_to_mission_giver();
324 break;
325 default:
326 //Suppress warnings
327 break;
328 }
329 }
330
wrap_up()331 void mission::wrap_up()
332 {
333 avatar &player_character = get_avatar();
334 if( player_character.getID() != player_id ) {
335 // This is called from npctalk.cpp, the npc should only offer the option to wrap up mission
336 // that have been assigned to the current player.
337 debugmsg( "mission::wrap_up called, player %d was assigned, but current player is %d",
338 player_id.get_value(), player_character.getID().get_value() );
339 }
340
341 status = mission_status::success;
342 player_character.on_mission_finished( *this );
343 std::vector<item_comp> comps;
344 switch( type->goal ) {
345 case MGOAL_FIND_ITEM_GROUP: {
346 inventory tmp_inv = player_character.crafting_inventory();
347 std::vector<item *> items = std::vector<item *>();
348 tmp_inv.dump( items );
349 item_group_id grp_type = type->group_id;
350 itype_id container = type->container_id;
351 bool specific_container_required = !container.is_null();
352 bool remove_container = type->remove_container;
353 itype_id empty_container = type->empty_container;
354
355 std::map<itype_id, int> matches = std::map<itype_id, int>();
356 get_all_item_group_matches(
357 items, grp_type, matches,
358 container, itype_id( "null" ), specific_container_required );
359
360 for( std::pair<const itype_id, int> &cnt : matches ) {
361 comps.push_back( item_comp( cnt.first, cnt.second ) );
362
363 }
364
365 player_character.consume_items( comps );
366
367 if( remove_container ) {
368 std::vector<item_comp> container_comp = std::vector<item_comp>();
369 if( !empty_container.is_null() ) {
370 container_comp.push_back( item_comp( empty_container, type->item_count ) );
371 player_character.consume_items( container_comp );
372 } else {
373 container_comp.push_back( item_comp( container, type->item_count ) );
374 player_character.consume_items( container_comp );
375 }
376 }
377 }
378 break;
379
380 case MGOAL_FIND_ITEM: {
381 const item item_sought( type->item_id );
382 if( item_sought.is_software() ) {
383 int consumed = 0;
384 while( consumed < item_count ) {
385 if( player_character.consume_software_container( type->item_id ) ) {
386 consumed++;
387 } else {
388 debugmsg( "Tried to consume more software %s than available", type->item_id.c_str() );
389 break;
390 }
391 }
392 } else {
393 comps.push_back( item_comp( type->item_id, item_count ) );
394 player_character.consume_items( comps );
395 }
396 }
397 break;
398 case MGOAL_FIND_ANY_ITEM:
399 player_character.remove_mission_items( uid );
400 break;
401 default:
402 //Suppress warnings
403 break;
404 }
405
406 type->end( this );
407 }
408
is_complete(const character_id & _npc_id) const409 bool mission::is_complete( const character_id &_npc_id ) const
410 {
411 if( status == mission_status::success ) {
412 return true;
413 }
414
415 avatar &player_character = get_avatar();
416 switch( type->goal ) {
417 case MGOAL_GO_TO: {
418 const tripoint_abs_omt cur_pos = player_character.global_omt_location();
419 return ( rl_dist( cur_pos, target ) <= 1 );
420 }
421
422 case MGOAL_GO_TO_TYPE: {
423 const auto cur_ter = overmap_buffer.ter( player_character.global_omt_location() );
424 return is_ot_match( type->target_id.str(), cur_ter, ot_match_type::type );
425 }
426
427 case MGOAL_FIND_ITEM_GROUP: {
428 inventory tmp_inv = player_character.crafting_inventory();
429 std::vector<item *> items = std::vector<item *>();
430 tmp_inv.dump( items );
431 item_group_id grp_type = type->group_id;
432 itype_id container = type->container_id;
433 bool specific_container_required = !container.is_null();
434
435 std::map<itype_id, int> matches = std::map<itype_id, int>();
436 get_all_item_group_matches(
437 items, grp_type, matches,
438 container, itype_id( "null" ), specific_container_required );
439
440 int total_match = std::accumulate( matches.begin(), matches.end(), 0,
441 []( const std::size_t previous, const std::pair<const itype_id, std::size_t> &p ) {
442 return static_cast<int>( previous + p.second );
443 } );
444
445 if( total_match >= ( type->item_count ) ) {
446 return true;
447
448 }
449 }
450 return false;
451
452 case MGOAL_FIND_ITEM: {
453 if( npc_id.is_valid() && npc_id != _npc_id ) {
454 return false;
455 }
456 item item_sought( type->item_id );
457 map &here = get_map();
458 int found_quantity = 0;
459 bool charges = item_sought.count_by_charges();
460 bool software = item_sought.is_software();
461 auto count_items = [this, &found_quantity, &player_character, charges, software]( item_stack &&
462 items ) {
463 for( const item &i : items ) {
464 if( !i.is_owned_by( player_character, true ) ) {
465 continue;
466 }
467 if( software ) {
468 for( const item *soft : i.softwares() ) {
469 if( soft->typeId() == type->item_id ) {
470 found_quantity ++;
471 }
472 }
473 }
474 if( charges ) {
475 found_quantity += i.charges_of( type->item_id, item_count - found_quantity );
476 } else {
477 found_quantity += i.amount_of( type->item_id, item_count - found_quantity );
478 }
479 }
480 };
481 for( const tripoint &p : here.points_in_radius( player_character.pos(), 5 ) ) {
482 if( player_character.sees( p ) ) {
483 if( here.has_items( p ) && here.accessible_items( p ) ) {
484 count_items( here.i_at( p ) );
485 }
486 if( const cata::optional<vpart_reference> vp =
487 here.veh_at( p ).part_with_feature( "CARGO", true ) ) {
488 count_items( vp->vehicle().get_items( vp->part_index() ) );
489 }
490 if( found_quantity >= item_count ) {
491 break;
492 }
493 }
494 }
495 if( software ) {
496 found_quantity += player_character.count_softwares( type->item_id );
497 }
498 if( charges ) {
499 return player_character.charges_of( type->item_id ) + found_quantity >= item_count;
500 } else {
501 return player_character.amount_of( type->item_id ) + found_quantity >= item_count;
502 }
503 }
504 return true;
505
506 case MGOAL_FIND_ANY_ITEM:
507 return player_character.has_mission_item( uid ) && ( !npc_id.is_valid() || npc_id == _npc_id );
508
509 case MGOAL_FIND_MONSTER:
510 if( npc_id.is_valid() && npc_id != _npc_id ) {
511 return false;
512 }
513 return g->get_creature_if( [&]( const Creature & critter ) {
514 const monster *const mon_ptr = dynamic_cast<const monster *>( &critter );
515 return mon_ptr && mon_ptr->mission_ids.count( uid );
516 } );
517
518 case MGOAL_RECRUIT_NPC: {
519 npc *p = g->find_npc( target_npc_id );
520 return p != nullptr && p->get_attitude() == NPCATT_FOLLOW;
521 }
522
523 case MGOAL_RECRUIT_NPC_CLASS: {
524 const auto npcs = overmap_buffer.get_npcs_near_player( 100 );
525 for( const auto &npc : npcs ) {
526 if( npc->myclass == recruit_class && npc->get_attitude() == NPCATT_FOLLOW ) {
527 return true;
528 }
529 }
530 return false;
531 }
532
533 case MGOAL_FIND_NPC:
534 return npc_id == _npc_id;
535
536 case MGOAL_TALK_TO_NPC:
537 case MGOAL_ASSASSINATE:
538 case MGOAL_KILL_MONSTER:
539 case MGOAL_COMPUTER_TOGGLE:
540 return step >= 1;
541
542 case MGOAL_KILL_MONSTER_TYPE:
543 return g->get_kill_tracker().kill_count( monster_type ) >= kill_count_to_reach;
544
545 case MGOAL_KILL_MONSTER_SPEC:
546 return g->get_kill_tracker().kill_count( monster_species ) >= kill_count_to_reach;
547
548 case MGOAL_CONDITION: {
549 // For now, we only allow completing when talking to the mission originator.
550 if( npc_id != _npc_id ) {
551 return false;
552 }
553
554 npc *n = g->find_npc( _npc_id );
555 if( n == nullptr ) {
556 return false;
557 }
558
559 mission_goal_condition_context cc;
560 cc.alpha = get_talker_for( player_character );
561 cc.beta = get_talker_for( *n );
562
563 for( auto &mission : n->chatbin.missions_assigned ) {
564 if( mission->get_assigned_player_id() == player_character.getID() ) {
565 cc.missions_assigned.push_back( mission );
566 }
567 }
568
569 return type->test_goal_condition( cc );
570 }
571
572 default:
573 return false;
574 }
575 }
576
get_all_item_group_matches(std::vector<item * > & items,item_group_id & grp_type,std::map<itype_id,int> & matches,const itype_id & required_container,const itype_id & actual_container,bool & specific_container_required)577 void mission::get_all_item_group_matches( std::vector<item *> &items,
578 item_group_id &grp_type, std::map<itype_id, int> &matches,
579 const itype_id &required_container, const itype_id &actual_container,
580 bool &specific_container_required )
581 {
582 for( item *itm : items ) {
583 bool correct_container = ( required_container == actual_container ) ||
584 !specific_container_required;
585
586 bool item_in_group = item_group::group_contains_item( grp_type, itm->typeId() );
587
588 //check whether item itself is target
589 if( item_in_group && correct_container ) {
590 std::map<itype_id, int>::iterator it = matches.find( itm->typeId() );
591 if( it != matches.end() ) {
592 it->second = ( it->second ) + 1;
593 } else {
594 matches.insert( std::make_pair( itm->typeId(), 1 ) );
595 }
596 }
597
598 //recursively check item contents for target
599 if( itm->is_container() && !itm->is_container_empty() ) {
600 std::list<item *> content_list = itm->contents.all_items_top();
601 std::vector<item *> content = std::vector<item *>();
602
603 //list of item into list item*
604 std::transform(
605 content_list.begin(), content_list.end(),
606 std::back_inserter( content ),
607 []( item * p ) {
608 return p;
609 } );
610
611 get_all_item_group_matches(
612 content, grp_type, matches,
613 required_container, ( itm->typeId() ), specific_container_required );
614 }
615 }
616 }
617
has_deadline() const618 bool mission::has_deadline() const
619 {
620 return deadline != calendar::turn_zero;
621 }
622
get_deadline() const623 time_point mission::get_deadline() const
624 {
625 return deadline;
626 }
627
get_description() const628 std::string mission::get_description() const
629 {
630 return type->description.translated();
631 }
632
has_target() const633 bool mission::has_target() const
634 {
635 return target != overmap::invalid_tripoint;
636 }
637
get_target() const638 const tripoint_abs_omt &mission::get_target() const
639 {
640 return target;
641 }
642
get_type() const643 const mission_type &mission::get_type() const
644 {
645 if( type == nullptr ) {
646 debugmsg( "Null mission type" );
647 return mission_type::get_all().front();
648 }
649
650 return *type;
651 }
652
has_follow_up() const653 bool mission::has_follow_up() const
654 {
655 return !follow_up.is_null();
656 }
657
get_follow_up() const658 mission_type_id mission::get_follow_up() const
659 {
660 return follow_up;
661 }
662
get_value() const663 int mission::get_value() const
664 {
665 return value;
666 }
667
get_id() const668 int mission::get_id() const
669 {
670 return uid;
671 }
672
get_item_id() const673 const itype_id &mission::get_item_id() const
674 {
675 return item_id;
676 }
677
has_failed() const678 bool mission::has_failed() const
679 {
680 return status == mission_status::failure;
681 }
682
in_progress() const683 bool mission::in_progress() const
684 {
685 return status == mission_status::in_progress;
686 }
687
process()688 void mission::process()
689 {
690 if( !in_progress() ) {
691 return;
692 }
693
694 if( has_deadline() && calendar::turn > deadline ) {
695 fail();
696 } else if( !npc_id.is_valid() && is_complete( npc_id ) ) { // No quest giver.
697 wrap_up();
698 }
699 }
700
get_npc_id() const701 character_id mission::get_npc_id() const
702 {
703 return npc_id;
704 }
705
get_likely_rewards() const706 const std::vector<std::pair<int, itype_id>> &mission::get_likely_rewards() const
707 {
708 return type->likely_rewards;
709 }
710
has_generic_rewards() const711 bool mission::has_generic_rewards() const
712 {
713 return type->has_generic_rewards;
714 }
715
set_target(const tripoint_abs_omt & p)716 void mission::set_target( const tripoint_abs_omt &p )
717 {
718 target = p;
719 }
720
set_target_npc_id(const character_id & npc_id)721 void mission::set_target_npc_id( const character_id &npc_id )
722 {
723 target_npc_id = npc_id;
724 }
725
is_assigned() const726 bool mission::is_assigned() const
727 {
728 return player_id.is_valid();
729 }
730
get_assigned_player_id() const731 character_id mission::get_assigned_player_id() const
732 {
733 return player_id;
734 }
735
name()736 std::string mission::name()
737 {
738 if( type == nullptr ) {
739 return "NULL";
740 }
741 return type->tname();
742 }
743
mission_id()744 mission_type_id mission::mission_id()
745 {
746 if( type == nullptr ) {
747 return mission_type_id( "NULL" );
748 }
749 return type->id;
750 }
751
dialogue_for_topic(const std::string & in_topic) const752 std::string mission::dialogue_for_topic( const std::string &in_topic ) const
753 {
754 // The internal keys are pretty ugly, it's better to translate them here than globally
755 static const std::map<std::string, std::string> topic_translation = {{
756 { "TALK_MISSION_DESCRIBE", "describe" },
757 { "TALK_MISSION_DESCRIBE_URGENT", "describe" },
758 { "TALK_MISSION_OFFER", "offer" },
759 { "TALK_MISSION_ACCEPTED", "accepted" },
760 { "TALK_MISSION_REJECTED", "rejected" },
761 { "TALK_MISSION_ADVICE", "advice" },
762 { "TALK_MISSION_INQUIRE", "inquire" },
763 { "TALK_MISSION_SUCCESS", "success" },
764 { "TALK_MISSION_SUCCESS_LIE", "success_lie" },
765 { "TALK_MISSION_FAILURE", "failure" }
766 }
767 };
768
769 const auto &replacement = topic_translation.find( in_topic );
770 const std::string &topic = replacement != topic_translation.end() ? replacement->second : in_topic;
771
772 const auto &response = type->dialogue.find( topic );
773 if( response != type->dialogue.end() ) {
774 return response->second.translated();
775 }
776
777 return string_format( "Someone forgot to code this message id is %s, topic is %s!",
778 type->id.c_str(), topic.c_str() );
779 }
780
mission()781 mission::mission()
782 : deadline( 0 )
783 {
784 type = nullptr;
785 status = mission_status::yet_to_start;
786 value = 0;
787 uid = -1;
788 target = tripoint_abs_omt( tripoint_min );
789 item_id = itype_id::NULL_ID();
790 item_count = 1;
791 target_id = string_id<oter_type_t>::NULL_ID();
792 recruit_class = NC_NONE;
793 target_npc_id = character_id();
794 monster_type = mtype_id::NULL_ID();
795 monster_kill_goal = -1;
796 npc_id = character_id();
797 good_fac_id = -1;
798 bad_fac_id = -1;
799 step = 0;
800 player_id = character_id();
801 }
802
803 namespace io
804 {
805 template<>
enum_to_string(mission::mission_status data)806 std::string enum_to_string<mission::mission_status>( mission::mission_status data )
807 {
808 switch( data ) {
809 // *INDENT-OFF*
810 case mission::mission_status::yet_to_start: return "yet_to_start";
811 case mission::mission_status::in_progress: return "in_progress";
812 case mission::mission_status::success: return "success";
813 case mission::mission_status::failure: return "failure";
814 // *INDENT-ON*
815 case mission::mission_status::num_mission_status:
816 break;
817
818 }
819 debugmsg( "Invalid mission_status" );
820 abort();
821 }
822
823 } // namespace io
824
status_from_string(const std::string & s)825 mission::mission_status mission::status_from_string( const std::string &s )
826 {
827 return io::string_to_enum<mission::mission_status>( s );
828 }
829
status_to_string(mission::mission_status st)830 std::string mission::status_to_string( mission::mission_status st )
831 {
832 return io::enum_to_string<mission::mission_status>( st );
833 }
834